web-dev-qa-db-fra.com

Ce code source active une chaîne en C. Comment fait-il cela?

Je lis un code d'émulateur et j'ai contré quelque chose de vraiment étrange:

switch (reg){
    case 'eax':
    /* and so on*/
}

Comment est-ce possible? Je pensais que vous ne pouviez que switch sur les types intégraux. Y a-t-il une supercherie macro?

105
Ian Colton

(Vous êtes le seul à pouvoir répondre à la partie "Trucage de macros" - à moins que vous ne colliez plus de code. Cependant, les macros ne peuvent pas fonctionner beaucoup - formellement, vous n'êtes pas autorisé à redéfinir les mots-clés ; le comportement à faire est indéfini.)

Afin de rendre le programme lisible, le développeur spirituel exploite le comportement défini par la mise en oeuvre . 'eax' est pas une chaîne, mais une constante multi-caractères . Notez très attentivement les guillemets simples autour de eax. Très probablement, cela vous donne un int dans votre cas, unique à cette combinaison de caractères. (Très souvent, chaque caractère occupe 8 bits sur un 32 bits int). Et tout le monde sait que vous pouvez switch sur un int!

Enfin, une référence standard:

La norme C99 dit:

6.4.4.4p10: "La valeur d'une constante de caractère entier contenant plusieurs caractères (par exemple, 'ab'), ou contenant un caractère ou une séquence d'échappement ne mappant pas sur un caractère d'exécution à un octet, est définie par la mise en oeuvre. "

145
Bathsheba

Selon la norme C (6.8.4.2 L’instruction switch)

3 L'expression de chaque étiquette de casse doit être une expression constante entière ...

et (6.6 expressions constantes)

6 ne expression constante entière doit être de type entier et ne comporter que des opérandes constantes, des constantes d'énumération, constantes de caractère, des expressions sizeof dont les résultats sont des constantes de nombre entier et des constantes flottantes. qui sont les opérandes immédiats des moulages. Les opérateurs de casting dans une expression constante entière ne convertissent que les types arithmétiques en types entiers, sauf dans le cadre d'un opérande en opérateur sizeof.

Maintenant qu'est-ce que 'eax'?

La norme C (6.4.4.4 Constantes de caractère)

2 Une constante de caractère entier est séquence d'un ou de plusieurs caractères multi-octets entre guillemets simples, comme dans 'x' ...

Alors 'eax' est une constante de caractère entière conformément au paragraphe 10 de la même section

  1. ... La valeur d'une constante de caractère entier contenant plusieurs caractères (par exemple, 'ab'), ou contenant un caractère ou une séquence d'échappement ne mappant pas sur un caractère d'exécution codé sur un octet, est définie par la mise en oeuvre.

Donc, selon la citation mentionnée en premier, il peut s'agir d'un opérande d'une expression constante entière pouvant être utilisée comme étiquette de casse.

Faites attention à ce qu'une constante de caractère (entre guillemets simples) ait le type int et ne soit pas identique à un littéral de chaîne (une séquence de caractères entourée de doubles guillemets) comportant le type d'un tableau de caractères.

44
Vlad from Moscow

Comme d'autres l'ont déjà dit, il s'agit d'une constante int et sa valeur réelle est définie par l'implémentation.

Je suppose que le reste du code ressemble à quelque chose comme

if (SOMETHING)
    reg='eax';
...
switch (reg){
    case 'eax':
    /* and so on*/
}

Vous pouvez être sûr que "eax" dans la première partie a la même valeur que "eax" dans la deuxième partie, donc tout se passe bien, non? ... faux.

Dans un commentaire, @Davislor énumère quelques valeurs possibles pour 'eax':

... 0x65, 0x656178, 0x65617800, 0x786165, 0x6165, ou autre chose

Remarquez la première valeur potentielle? C'est juste 'e', en ignorant les deux autres caractères. Le problème est que le programme utilise probablement 'eax', 'ebx', etc. Si toutes ces constantes ont la même valeur que 'e' vous vous retrouvez avec

switch (reg){
    case 'e':
       ...
    case 'e':
       ...
    ...
}

Cela ne semble pas trop bon, n'est-ce pas?

La bonne partie de la "définition d'implémentation" est que le programmeur peut consulter la documentation de son compilateur et voir s'il fait quelque chose de sensé avec ces constantes. Si c'est le cas, libre à la maison.

La mauvaise partie est qu’un autre type pauvre peut prendre le code et essayer de le compiler en utilisant un autre compilateur. Erreur de compilation instantanée. Le programme n'est pas portable.

Comme @zwol l'a souligné dans les commentaires, la situation n'est pas aussi grave que je le pensais. Dans le cas contraire, le code ne compile pas. Cela vous donnera au moins un nom de fichier et un numéro de ligne exacts pour le problème. Cependant, vous n’avez pas de programme de travail.

12
Stig Hemmer

Le fragment de code utilise une curiosité historique appelée constante de plusieurs caractères, également appelée multi-caractères.

'eax' est une constante entière dont la valeur est définie par l'implémentation.

Voici une page intéressante sur les caractères multiples et sur la façon dont ils peuvent être utilisés mais ne devraient pas:

http://www.zipcon.net/~swhite/docs/computers/languages/c_multi-char_const.html


En regardant plus loin dans le rétroviseur, voici comment le manuel original de Dennis Ritchie de Dennis Ritchie datant du bon vieux temps ( https://www.bell-labs.com/usr/dmr/www/cman.pdf) ) constantes de caractère spécifiées.

2.3.2 Constantes de caractère

Une constante de caractère est composée de 1 ou 2 caractères placés entre guillemets '' '' ’’. Dans une constante de caractère, une citation doit être précédée d’une barre oblique inversée "‘ \ ’’. Certains caractères non graphiques et ‘‘ \ ’’ Elle-même, peut être échappé selon le tableau suivant:

    BS \b
    NL \n
    CR \r
    HT \t
    ddd \ddd
    \ \\

L'évasion ''\ddd ’’ Se compose d’une barre oblique inversée suivie de 1, 2 ou 3 chiffres octaux utilisés pour spécifier la valeur du caractère souhaité. Un cas particulier de cette construction est "_ \0 ’’ (Non suivi d’un chiffre) qui indique un caractère nul.

Les constantes de caractère se comportent exactement comme des entiers (en particulier, pas comme des objets de type caractère). Conformément à la structure d'adressage du PDP-11, une constante de caractère de longueur 1 a le code du caractère donné dans l'octet de poids faible et de 0 dans l'octet de poids fort; une constante de caractère de longueur 2 a le code du premier caractère de l'octet de poids faible et celui du deuxième caractère de l'octet de poids fort. Les constantes de caractère avec plus d'un caractère dépendent intrinsèquement de la machine et doivent être évitées.

La dernière phrase est tout ce dont vous avez besoin de vous rappeler à propos de cette curieuse construction: Les constantes de caractère avec plus d'un caractère dépendent intrinsèquement de la machine et doivent être évitées.

1
chqrlie