Opérations arithmétiques entre constantes
-
09-12-2019 - |
Question
Considérez ce code ;
#define A 5
#define B 3
int difference = A - B;
la valeur de « différence » est-elle codée en dur comme « 2 » au moment de la compilation, ou est-elle calculée au moment de l'exécution ?
La solution
Le A
et B
les macros sont un peu une distraction.Ce:
#define A 5
#define B 3
int difference = A - B;
est exactement équivalent à ceci :
int difference = 5 - 3;
alors parlons de ce dernier.
5 - 3
est un expression constante, qui est une expression qui "peut être évaluée pendant la traduction plutôt que lors de l'exécution, et peut donc être utilisée partout où peut se trouver une constante".C'est aussi une *expression constante entière".Par exemple, une étiquette de cas doit être une expression constante entière, vous pouvez donc écrire ceci :
switch (foo) {
case 2: /* this is a constant */
...
}
ou ca:
switch (foo) {
case 5 - 3: /* this is a constant expression */
...
}
Mais notez que la définition dit qu'il peut être évalué pendant la traduction, pas que cela doive l'être.Certains contextes nécessitent des expressions constantes, et dans ces contextes, l'expression doit être évalué au moment de la compilation.
Mais en supposant que difference
est déclaré dans une fonction, l'initialiseur ne fait pas partie de ces contextes.
Tout compilateur valant ce que vous payez (même s'il est gratuit) réduira 5 - 3
à 2
au moment de la compilation et générer du code qui stocke la valeur 2
dans difference
.Mais ce n’est pas obligatoire.La norme C précise le comportement de programmes ;il ne précise pas comment ce comportement doit être implémenté.Mais il est prudent de supposer que quel que soit le compilateur que vous utilisez, il remplacera 5 - 3
par 2
.
Même si vous écrivez :
int difference = 2;
un compilateur pourrait légalement générer du code qui charge la valeur 5
dans un registre, soustrait 3
à partir de celui-ci et stocke le contenu du registre dans difference
.Ce serait une chose idiote à faire, mais la norme linguistique ne l'exclut pas.
Tant que le résultat final est celui difference
a la valeur 2
, la norme linguistique ne se soucie pas de la manière dont cela est fait.
Par contre, si vous écrivez :
switch (foo) {
case 5 - 3: /* ... */
case 2: /* ... */
}
puis le compilateur doit calculez le résultat afin qu'il puisse diagnostiquer l'erreur (vous ne pouvez pas avoir deux étiquettes de cas avec la même valeur.
Enfin, si vous définissez difference
à la portée du fichier (en dehors de toute fonction), puis la valeur initiale fait il faut être constant.Mais la véritable distinction dans ce cas n'est pas de savoir si 5 - 3
sera évalué au moment de la compilation, il s'agit de savoir si vous êtes autorisé pour utiliser une expression non constante.
Référence:La dernière version de la norme C 2011 est N1570 (grand PDF);les expressions constantes sont discutées dans la section 6.6.
Autres conseils
La norme ne précise pas ce genre de chose.Cela ne dit rien sur de potentielles optimisations comme celle-ci (et pour cause).Une norme définit la sémantique, pas la mise en œuvre).
Pourquoi ne pas regarder le démontage de votre compilateur ?Cela vous donnera une réponse définitive.
...
Alors faisons ça.
Voici le résultat de VC++ 10 :
#include <iostream>
#define A 5
#define B 3
int main() {
int x = A - B;
std::cout << x; // make sure the compiler doesn't toss it away
010A1000 mov ecx,dword ptr [__imp_std::cout (10A2048h)]
010A1006 push 2
010A1008 call dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (10A2044h)]
return 0;
010A100E xor eax,eax
Comme vous pouvez le voir, cela vient de remplacer l'occurrence de x
avec une valeur statique de 2 et l'a poussé sur la pile pour l'appel à cout
.Il n'a pas évalué l'expression au moment de l'exécution.