Le besoin de parenthèses dans les macros en C [dupliquer]
cette question a déjà une réponse ici:
j'ai essayé de jouer avec la définition de la macro SQR
dans le code suivant:
#define SQR(x) (x*x)
int main()
{
int a, b=3;
a = SQR(b+5); // Ideally should be replaced with (3+5*5+3), though not sure.
printf("%dn",a);
return 0;
}
il imprime 23
. Si Je changer la macro définition à SQR(x) ((x)*(x))
puis la sortie est comme prévu, 64
. Je sais qu'un appel à une macro en C remplace l'appel par la définition de la macro, mais je ne comprends toujours pas, comment il a calculé 23
.
8 réponses
les macros de pré-processeur effectuent le remplacement du texte avant que le code ne soit compilé ainsi
SQR(b+5)
se traduit par
(b+5*b+5) = (6b+5) = 6*3+5 = 23
les appels de fonction réguliers calculeraient la valeur du paramètre (b+3) avant de le passer à la fonction, mais puisqu'une macro est un remplacement précompilé, l'ordre algébrique des opérations devient très important.
parce que (3+5*3+5 == 23)
.
alors que ((3+5)*(3+5)) == 64
.
la meilleure façon de faire cela est de ne pas utiliser une macro :
inline int SQR(int x) { return x*x; }
ou écrivez simplement x*x
.
envisager le remplacement macro en utilisant cette macro:
#define SQR(x) (x*x)
utilisant b+5
comme argument. Faire le remplacement vous-même. Dans votre code, SQR(b+5)
deviendra: (b+5*b+5)
, ou (3+5*3+5)
. Maintenant, rappelez-vous votre opérateur priorité règles: *
avant +
. C'est évalué comme: (3+15+5)
, ou 23
.
la deuxième version de la macro:
#define SQR(x) ((x) * (x))
est correct, parce que vous utilisez le parens pour développer vos arguments macro à partir des effets de priorité de l'opérateur.
Cette page , expliquant opérateur préférence pour le C a un beau tableau. Voici de la section pertinente de la C11 document de référence.
la chose à se rappeler ici est que vous devriez prendre l'habitude de toujours protéger les arguments dans vos macros, à l'aide de parenthèses.
la macro se développe en
a = b+5*b+5;
c'est à dire
a = b + (5*b) + 5;
Donc 23.
une macro n'est qu'une simple substitution de texte. Après prétraitement, votre code ressemble à:
int main()
{
int a, b=3;
a = b+5*b+5;
printf("%d\n",a);
return 0;
}
La Multiplication a un opérateur de priorité plus élevé que l'addition, donc c'est fait avant les deux additions lors du calcul de la valeur pour a
. Ajouter des parenthèses à votre définition de macro corrige le problème en le faisant:
int main()
{
int a, b=3;
a = (b+5)*(b+5);
printf("%d\n",a);
return 0;
}
les opérations entre parenthèses sont évaluées avant la multiplication, de sorte que les additions arrive d'abord maintenant, et vous obtenez le résultat a = 64
que vous attendez.
Après prétraitement, SQR(b+5)
sera étendu à (b+5*b+5)
. Ce n'est évidemment pas correct.
il y a deux erreurs communes dans la définition de SQR
:
-
ne pas inclure les arguments de macro entre parenthèses dans le corps de macro, donc si ces arguments sont des expressions, les opérateurs avec des précédents différents dans ces expressions peuvent causer des problèmes. Voici une version qui a corrigé ce problème ""
#define SQR(x) ((x)*(x))
-
évaluer les arguments de macro plus d'une fois, donc si ces arguments sont des expressions qui ont des effets secondaires, ces effets secondaires pourraient être pris plus d'une fois. Par exemple, considérons le résultat de
SQR(++x)
.en utilisant GCC type de extension, ce problème peut être corrigé comme ceci
#define SQR(x) ({ typeof (x) _x = (x); _x * _x; })
ces Deux problèmes pourraient être résolus par remplacement de cette macro par une fonction en ligne
inline int SQR(x) { return x * x; }
cela nécessite une extension en ligne GCC ou C99, voir 6.40 une fonction en ligne est aussi rapide qu'une Macro .
parce que les Macros ne sont que des remplacements de chaîne et cela se produit avant le processus d'achèvement. Le compilateur n'aura pas la chance de voir la variable Macro et sa valeur. Par exemple: si une macro est définie comme
#define BAD_SQUARE(x) x * x
et appelé comme ceci
BAD_SQUARE(2+1)
le compilateur verra ce
2 + 1 * 2 + 1
ce qui entraînera, peut-être, un résultat inattendu de
5
à corrigez ce comportement, vous devriez toujours entourer les macro-variables de parenthèses, comme
#define GOOD_SQUARE(x) (x) * (x)
quand cette macro est appelée, par exemple, comme ceci
GOOD_SQUARE(2+1)
le compilateur verra ce
(2 + 1) * (2 + 1)
qui se traduira par
9
en outre, voici un exemple complet pour illustrer davantage le point
#include <stdio.h>
#define BAD_SQUARE(x) x * x
// In macros alsways srround the variables with parenthesis
#define GOOD_SQUARE(x) (x) * (x)
int main(int argc, char const *argv[])
{
printf("BAD_SQUARE(2) = : %d \n", BAD_SQUARE(2) );
printf("GOOD_SQUARE(2) = : %d \n", GOOD_SQUARE(2) );
printf("BAD_SQUARE(2+1) = : %d ; because the macro will be \
subsituted as 2 + 1 * 2 + 1 \n", BAD_SQUARE(2+1) );
printf("GOOD_SQUARE(2+1) = : %d ; because the macro will be \
subsituted as (2 + 1) * (2 + 1) \n", GOOD_SQUARE(2+1) );
return 0;
}
il suffit de mettre entre parenthèses chaque argument de la macro expansion.
#define SQR(x) ((x)*(x))
cela fonctionnera pour n'importe quel argument ou valeur que vous passez.