Que sont les expressions ayant des effets secondaires et pourquoi ne devraient-elles pas être transmises à une macro?

Je suis tombé sur une déclaration dans le texte C Comment programmer:

“Les expressions avec des effets secondaires (c’est-à-dire que les valeurs de variables sont modifiées) ne doivent pas être transmises à une macro, car les arguments de la macro peuvent être évalués plus d’une fois.”.

Ma question est de savoir quelles sont les expressions avec des effets secondaires et pourquoi ne devraient-elles pas être transmises à une macro?

L’exemple classique est une macro permettant de calculer le maximum de deux valeurs:

#define MAX(a, b) ((a) > (b) ? (a) : (b)) 

Maintenant, appelons la macro comme ceci:

 int x = 5; int y = 7; int z = MAX(x++, y++); 

Maintenant, si MAX était une fonction normale, nous nous attendrions à ce que x et y soient incrémentés une fois, n’est-ce pas? Cependant, parce que c’est une macro, “l’appel” est remplacé comme ceci:

 int z = ((x++) > (y++) ? (x++) : (y++)); 

Comme vous le voyez, la variable y sera incrémentée deux fois , une fois dans la condition et une fois en tant que résultat final de l’ opérateur ternaire .

Ceci est le résultat d’une expression avec des effets secondaires (l’expression post-incrémentation) et d’un développement de macro.


Sur une note connexe, il existe également d’autres dangers avec les macros. Par exemple, prenons cette simple macro:

 #define MUL_BY_TWO(x) (x * 2) 

Ça a l’air simple, non? Mais maintenant, si nous l’utilisions comme ceci:

 int result = MUL_BY_TWO(a + b); 

Cela va s’étendre comme

 int result = (a + b * 2); 

Et comme vous le savez, vous savez que la multiplication a une priorité plus élevée que l’addition, l’expression a + b * 2 est équivalente à a + (b * 2) , probablement pas ce que le rédacteur de macro avait voulu. C’est pourquoi les arguments relatifs aux macros doivent être placés entre leurs propres parenthèses:

 #define MUL_BY_TWO(x) ((x) * 2) 

Ensuite, l’expansion sera

 int result = ((a + b) * 2); 

ce qui est probablement correct.

Pour le dire simplement un effet secondaire est une écriture sur un object ou une lecture d’un object volatile.

Donc, un exemple d’effet secondaire:

 i++; 

Voici l’utilisation d’un effet secondaire dans une macro:

 #define MAX(a, b) ((a) > (b)) ? (a) : (b)) int a = 42; int b = 1; int c; c = MAX(a, b++); 

Le danger est contraire à une fonction où les arguments sont passés par valeur que vous modifiez potentiellement un object b une ou deux fois (en fonction des arguments de la macro, ici une fois) dans la macro en raison du fonctionnement des macros (en remplaçant les jetons b++ dans la définition de macro).

Les effets secondaires peuvent être définis comme suit:

L’évaluation d’une expression produit quelque chose et si, en plus, l’état de l’environnement d’exécution change, on dit que l’expression (son évaluation) a des effets secondaires. Par exemple:

  int x = y++; //where y is also an int 

En plus de l’opération d’initialisation, la valeur de y est modifiée en raison de l’effet secondaire de l’opérateur ++.

Considérons maintenant une macro pour la quadrature d’un entier:

  #define Sq(a) a*a main() { int a=6; int res=Sq(a); printf("%d\n",res); res=Sq(++a); printf("%d",res); } 

Vous vous attendriez à ce que la sortie soit

36 49

Cependant, la sortie est

36 64

parce que la macro entraîne un remplacement textuel et

res devient (++ a) * (++ a) c’est-à-dire 8 * 8 = 64

Par conséquent, nous ne devrions pas transmettre d’arguments avec des effets secondaires aux macros. ( http://ideone.com/mMPQLP )