La necesidad de paréntesis en macros en C

Intenté jugar con la definición de macro SQR en el siguiente código:

 #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("%d\n",a); return 0; } 

Imprime 23 . Si cambio la definición de macro a SQR(x) ((x)*(x)) entonces la salida es la esperada, 64 . Sé que una llamada a una macro en C reemplaza la llamada con la definición de la macro, pero todavía no puedo entender cómo calculó 23 .

Las macros del preprocesador realizan el reemplazo de texto antes de comstackr el código, por lo que SQR(b+5) traduce a (b + 5 * b + 5) = (6b + 5) = 6 * 3 + 5 = 23

Las llamadas a funciones regulares calcularían el valor del parámetro (b + 3) antes de pasarlo a la función, pero como una macro es un reemplazo precomstackdo, el orden algebraico de las operaciones se vuelve muy importante.

Porque (3+5*3+5 == 23) .

Considerando que ((3+5)*(3+5)) == 64 .

La mejor manera de hacerlo es no utilizar una macro :

 inline int SQR(int x) { return x*x; } 

O simplemente escribe x*x .

Considere la macro sustitución utilizando esta macro:

 #define SQR(x) (x*x) 

Usando b+5 como argumento Haz el reemplazo tú mismo. En su código, SQR(b+5) se convertirá en: (b+5*b+5) , o (3+5*3+5) . Ahora recuerde las reglas de precedencia de su operador : * antes de + . Entonces esto se evalúa como: (3+15+5) , o 23 .

La segunda versión de la macro:

 #define SQR(x) ((x) * (x)) 

Es correcto, porque está utilizando los parens para proyectar sus argumentos de macro a partir de los efectos de la precedencia del operador.

Esta página que explica la preferencia del operador para C tiene un buen cuadro. Aquí está la sección relevante del documento de referencia C11.

Lo que hay que recordar aquí es que debes tener el hábito de proteger siempre cualquier argumento en tus macros, usando parens.

La macro se expande a

  a = b+5*b+5; 

es decir

  a = b + (5*b) + 5; 

Entonces 23.

Una macro es solo una sustitución de texto directo. Después del preprocesamiento, su código se ve así:

 int main() { int a, b=3; a = b+5*b+5; printf("%d\n",a); return 0; } 

La multiplicación tiene una precedencia de operador superior a la sum, por lo que se hace antes de las dos adiciones cuando se calcula el valor de a . Agregar paréntesis a su definición de macro corrige el problema haciéndolo:

 int main() { int a, b=3; a = (b+5)*(b+5); printf("%d\n",a); return 0; } 

Las operaciones entre paréntesis se evalúan antes de la multiplicación, por lo que las adiciones suceden primero ahora, y obtienes el resultado a = 64 que esperas.

Después del preprocesamiento, SQR(b+5) se expandirá a (b+5*b+5) . Esto obviamente no es correcto.

Hay dos errores comunes en la definición de SQR :

  1. no encierre los argumentos de macro entre paréntesis en el cuerpo de la macro, de modo que si esos argumentos son expresiones, los operadores con diferentes precedencias en esas expresiones pueden causar problemas. Aquí hay una versión que solucionó este problema

     #define SQR(x) ((x)*(x)) 
  2. evalúa los argumentos de macro más de una vez, por lo que si esos argumentos son expresiones que tienen efectos secundarios, esos efectos secundarios podrían tomarse más de una vez. Por ejemplo, considere el resultado de SQR(++x) .

    Al usar el tipo de extensión GCC, este problema puede solucionarse así

     #define SQR(x) ({ typeof (x) _x = (x); _x * _x; }) 

Ambos problemas podrían solucionarse reemplazando esa macro con una función en línea

  inline int SQR(x) { return x * x; } 

Esto requiere la extensión en línea de GCC o C99, consulte 6.40. Una función en línea es tan rápida como una macro .

Debido a que las macros son solo un reemplazo de cadenas y sucede antes del proceso de finalización. El comstackdor no tendrá la oportunidad de ver la variable Macro y su valor. Por ejemplo: si una macro se define como

 #define BAD_SQUARE(x) x * x 

y llamado así

 BAD_SQUARE(2+1) 

el comstackdor verá esto

 2 + 1 * 2 + 1 

que dará como resultado, tal vez, el resultado inesperado de

 5 

Para corregir este comportamiento, siempre debe rodear las macrovariables con paréntesis, como

 #define GOOD_SQUARE(x) (x) * (x) 

cuando se llama a esta macro, por ejemplo, como esta

 GOOD_SQUARE(2+1) 

el comstackdor verá esto

 (2 + 1) * (2 + 1) 

que dará como resultado

 9 

Además, aquí está un ejemplo completo para ilustrar aún más el punto

 #include  #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; } 

Simplemente encierre todos y cada uno de los argumentos en la macro expansión entre paréntesis.

 #define SQR(x) ((x)*(x)) 

Esto funcionará para cualquier argumento o valor que pase.