41 votes

Pourquoi le comportement de la soustraction de caractères est-il spécifique à l'implémentation ?

Cette déclaration :

if('z' - 'a' == 25)

n'est pas garanti d'être évalué de la même manière. Elle dépend du compilateur. De plus, il n'est pas garanti qu'elle soit évaluée de la même manière que celle-ci :

#if 'z' - 'a' == 25

même si le préprocesseur et le compilateur sont exécutés sur la même machine. Pourquoi cela ?

2 votes

Dans le premier cas, le préprocesseur fait le travail

8 votes

La même chose que quoi ? Est-ce que vous comparez #if 'z' - 'a' == 25 avec if('z' - 'a' == 25) ou vous demandez pourquoi 'z' - 'a' n'est pas évalué à la même constante sur différentes implémentations.

0 votes

Je demande pourquoi il n'est pas évalué à la même constante sur différentes implémentations. J'ai clairement mentionné dans ma question d'évaluer et non de comparer. La réponse de Klutt est ce que je voulais. Je dois attendre une certaine période de temps pour accepter sa réponse, c'est pourquoi j'attends.

69voto

Zack Points 44583

L'OP demande une citation directe de la norme - N1570 §6.10.1p3,4 + note de bas de page 168 :

... l'expression de la constante de contrôle est évaluée selon les règles de 6.6. ... Cela inclut l'interprétation des constantes de caractères, ce qui peut impliquer la conversion de séquences d'échappement en membres du jeu de caractères d'exécution. Le fait que la valeur numérique de ces constantes de caractère corresponde à la valeur obtenue lorsqu'une constante de caractère identique apparaît dans une expression (autrement que dans une directive #if ou #elif) est défini par l'implémentation. 168

[Ainsi, l'expression constante dans la directive #if et l'instruction if suivantes n'est pas garantie d'être évaluée à la même valeur dans ces deux contextes.

#if 'z' - 'a' == 25
if ('z' - 'a' == 25)

Donc, oui, ce n'est vraiment pas garanti.

Pour comprendre pourquoi ce n'est pas garanti, il faut d'abord savoir que la norme C n'exige pas les constantes de caractère 'a' y 'z' pour avoir les valeurs numériques attribuées à ces caractères par ASCII. Le plus Les implémentations C utilisent aujourd'hui l'ASCII ou un superset, mais il existe un autre encodage appelé EBCDIC qui est encore largement utilisé (uniquement sur les mainframes IBM, mais il y en a encore beaucoup). En EBCDIC, non seulement 'a' y 'z' ont des valeurs différentes de l'ASCII, l'alphabet n'est pas une séquence contiguë ! C'est pourquoi l'expression 'z' - 'a' == 25 pourrait ne pas évaluer la vérité en premier lieu.

Vous devez également savoir que la norme C tente de maintenir une distinction entre le codage de texte utilisé pour le code source (le "jeu de caractères source") et le codage de texte que le programme utilisera lors de l'exécution (le "jeu de caractères d'exécution"). C'est ainsi que vous pouvez, au moins en principe, prendre un programme dont le code source est en ASCII et l'exécuter sans modification sur un ordinateur qui utilise EBCDIC, simplement en effectuant une compilation croisée appropriée ; vous n'avez pas besoin de convertir d'abord le texte source en EBCDIC.

Aujourd'hui, le compilateur doit comprendre les deux jeux de caractères s'ils sont différents, mais historiquement, le préprocesseur C ( phases de traduction 1 à 4) et le "compilateur proprement dit" (phases 5 à 7) étaient deux programmes distincts, et #if sont le seul endroit où le préprocesseur doit connaître le jeu de caractères d'exécution. Ainsi, en faisant en sorte que l'implémentation définisse si le "jeu de caractères d'exécution" utilisé par le préprocesseur correspond à celui utilisé par le compilateur proprement dit, la norme autorise le préprocesseur à faire tout son travail dans le jeu de caractères d'exécution. source ce qui a rendu la vie un peu plus facile en 1989.

Cela dit, je serais très surpris de trouver un compilateur moderne qui ne fasse pas en sorte que les deux expressions soient évaluées à la même valeur, même lorsque les jeux de caractères d'exécution et de source sont grossièrement incompatibles. Les compilateurs modernes ont tendance à avoir intégré les préprocesseurs -- les phases 1 à 7 sont toutes exécutées par le même programme -- et même si ce n'est pas le cas, la charge d'ingénierie consistant à spécialiser le préprocesseur pour faire correspondre son jeu de caractères d'exécution au compilateur proprement dit est triviale de nos jours.

7 votes

Je ne savais pas que l'EBCDIC était "encore largement utilisé". Le nom EBCDIC me fait tellement penser aux cartes perforées que je ne le prends généralement que comme un exemple plutôt académique (comme pour les machines à mots de 36 bits ou le ternaire).

0 votes

Quelques nouveaux encodages ont été réalisés au cours de ce millénaire pour des raisons de rétrocompatibilité. L'encodage officiel UTF-EBCDIC ( unicode.org/reports/tr16 ) et UTFE d'Oracle sont tous deux sortis en 2002. Ils sont plus destinés à envoyer des données Unicode en toute sécurité à travers de vieux systèmes qui supposent que les données sont en EBCDIC, cependant. Quelqu'un les utilise-t-il vraiment ?

1 votes

Le compilateur C/C++ z/OS XL d'IBM suppose toujours la page de code 1047 par défaut, mais dispose d'options permettant de modifier le jeu de caractères pour tout ou partie de la source. En particulier, l'option CONVLIT peut ne pas affecter toutes les directives du préprocesseur, du moins à partir de cette version : ( www-304.ibm.com/servers/resourcelink/svc00100.nsf/pages/ )

16voto

Broman Points 5642

Parce que tous les ordinateurs n'utilisent pas l'ascii ou l'unicode.

Dans le passé, une norme appelée ebcdic était courante. Dans l'ebcdic 500, la valeur de 'z' est 169 et la valeur de 'a' est de 130. L'expression 'z'-'a' serait alors évalué à 39.

Cela explique pourquoi vous ne pouvez pas supposer une certaine valeur pour une expression du type 'a' ou même 'z'-'a' . Cependant, cela n'explique pas pourquoi les deux expressions de la Q ne sont pas garanties comme étant égales.

Le préprocesseur et le compilateur sont deux choses différentes. Le préprocesseur s'occupe de l'encodage utilisé dans le code source, tandis que le compilateur cible la machine pour laquelle vous compilez. Voir la réponse de zwol pour une explication plus élaborée.

3 votes

Pas seulement dans le passé, les mainframes IBM l'utilisent en permanence.

0 votes

Cela n'explique pas pourquoi la norme permet au préprocesseur et au compilateur proprement dit d'être en désaccord sur la valeur de l'expression.

1 votes

@zwol vous avez probablement mal compris la question (mal posée) (comme moi au début). Le PO indique clairement ce qu'il veut dans son dernier commentaire.

7voto

Lorehead Points 953

Pour développer les autres réponses correctes, un exemple réel de compilateur C non-ASCII qui est toujours utilisé est z/OS XL C/C++ d'IBM . Par défaut, il suppose que les fichiers sources sont en page de code IBM 1047 (la version d'EBCDIC avec le même répertoire que Latin-1). Cependant, il dispose de plusieurs options de compilation différentes pour prendre en charge non seulement l'ASCII, mais aussi le "code hybride", ou les fichiers sources contenant des données dans plus d'un encodage. (Ces programmes existent parce que les compilateurs MVS exigeaient que les déclarations syntaxiques soient uniquement en codage IBM-1047).

D'après la documentation, il semble qu'il soit possible d'utiliser des commandes telles que #pragma CONVLIT(suspend) de manière à ce que ces deux déclarations soient évaluées différemment sur ce compilateur. Je n'ai pas de copie pour tester un MCVE.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X