with que programacion macro defines define c macros

que - gcc macros



C Macro Pregunta-(x) vs(-x) (2)

Hay varios problemas relacionados que los paréntesis adicionales resuelven. Voy a ir a través de ellos uno por uno:

Prueba: int y = abs( a ) + 2

Asumamos que usas:

#define abs(x) (x<0)?-x:x ... int y = abs( a ) + 2

Esto se expande a int y = (a<0)?-a:a+2 . El +2 une solo al resultado falso. 2 solo se agrega cuando a es positivo, no cuando es negativo. Así que necesitamos paréntesis alrededor de todo el asunto:

#define abs(x) ( (x<0) ? -x : x )

Pruebe: int y = abs(a+b);

Pero entonces podríamos tener int y = abs(a+b) que se expande a int y = ( (a+b<0) ? -a+b : a+b) . Si a + b es negativo, b no se anula cuando se agregan al resultado. Así que necesitamos poner la x de -x entre paréntesis.

#define abs(x) ( (x<0) ? -(x) : x )

Prueba: int y = abs(a=b);

Esto debería ser legal (aunque malo), pero se expande a int y = ( (a=b<0)?-(a=b):a=b ); que trata de asignar la b final al ternario. Esto no debe compilar. (Tenga en cuenta que lo hace en C ++. Tuve que compilarlo con gcc en lugar de g ++ para ver que no se compilara con el error "lvalue en asignación" no válido).

#define abs(x) ( (x<0) ? -(x) : (x) )

Intente: int y = abs((a<b)?a:b);

Esto se expande a int y = ( ((a<b)?a:b<0) ? -((a<b)?a:b) : (a<b)?a:b ) , que agrupa a <0 con la b, no todo el ternario según lo previsto.

#define abs(x) ( ( (x) < 0) ? -(x) : (x) )

Al final, cada instancia de x es propensa a algún problema de agrupación que los paréntesis necesitan resolver.

Problema común: precedencia del operador

El hilo común en todos estos es la precedencia del operador : si coloca un operador en su invocación de abs(...) que tiene una precedencia más baja, entonces hay algo en torno a donde se usa x en la macro, entonces se enlazará incorrectamente. Por ejemplo, abs(a=b) se expandirá a a=b<0 que es lo mismo que a=(b<0) ... eso no es lo que quiso decir la persona que llama.

El "camino correcto" para implementar abs

Por supuesto, esta es la forma incorrecta de implementar abs de todos modos ... si no desea utilizar las funciones integradas (y debería hacerlo, ya que estarán optimizadas para cualquier hardware que conecte), entonces debería ser una plantilla en línea (si se usa C ++) por las mismas razones mencionadas cuando Meyers, Sutter y otros discuten la reimplementación de las funciones mín y máx. (Otras respuestas también lo han mencionado: ¿qué sucede con los abs(x++) ?)

Desde lo alto de mi cabeza, una implementación razonable podría ser:

template<typename T> inline const T abs(T const & x) { return ( x<0 ) ? -x : x; }

Aquí está bien dejar de lado los paréntesis, ya que sabemos que x es un valor único, no una expansión arbitraria de una macro.

Mejor aún, como señaló Chris Lutz en los comentarios a continuación, puede utilizar la especialización de plantillas para llamar a las versiones optimizadas (abs, fabs, labs) y obtener todos los beneficios de la seguridad de tipos, la compatibilidad con tipos no integrados y el rendimiento.

Código de prueba

#if 0 gcc $0 -g -ansi -std=c99 -o exe && ./exe exit #endif #include <stdio.h> #define abs1(x) (x<0)?-x:x #define abs2(x) ((x<0)?-x:x) #define abs3(x) ((x<0)?-(x):x) #define abs4(x) ((x<0)?-(x):(x)) #define abs5(x) (((x)<0)?-(x):(x)) #define test(x) printf("//%30s=%d/n", #x, x); #define testt(t,x) printf("//%15s%15s=%d/n", t, #x, x); int main() { test(abs1( 1)+2) test(abs1(-1)+2) // abs1( 1)+2=3 // abs1(-1)+2=1 test(abs2( 1+2)) test(abs2(-1-2)) // abs2( 1+2)=3 // abs2(-1-2)=-1 int a,b; //b = 1; testt("b= 1; ", abs3(a=b)) //b = -1; testt("b=-1; ", abs3(a=b)) // When compiled with -ansi -std=c99 options, this gives the errors: //./so1a.c: In function ''main'': //./so1a.c:34: error: invalid lvalue in assignment //./so1a.c:35: error: invalid lvalue in assignment // Abs of the smaller of a and b. Should be one or two. a=1; b=2; testt("a=1; b=2; ", abs4((a<b)?a:b)) a=2; b=1; testt("a=2; b=1; ", abs4((a<b)?a:b)) // abs4((a<b)?a:b)=-1 // abs4((a<b)?a:b)=1 test(abs5( 1)+2) test(abs5(-1)+2) test(abs5( 1+2)) test(abs5(-1-2)) b = 1; testt("b= 1; ", abs5(a=b)) b = -1; testt("b=-1; ", abs5(a=b)) a=1; b=2; testt("a=1; b=2; ", abs5((a<b)?a:b)) a=2; b=1; testt("a=2; b=1; ", abs5((a<b)?a:b)) }

Salida

abs1( 1)+2=3 abs1(-1)+2=1 abs2( 1+2)=3 abs2(-1-2)=-1 a=1; b=2; abs4((a<b)?a:b)=-1 a=2; b=1; abs4((a<b)?a:b)=1 abs5( 1)+2=3 abs5(-1)+2=3 abs5( 1+2)=3 abs5(-1-2)=3 b= 1; abs5(a=b)=1 b=-1; abs5(a=b)=1 a=1; b=2; abs5((a<b)?a:b)=1 a=2; b=1; abs5((a<b)?a:b)=1

Estoy revisando las respuestas de mi profesor y una pregunta fue:

La implementación correcta de una función como macro para valor absoluto es:

#define abs(x) ((x)<0 ? (-x) : (x)) #define abs(x) ((x)<0 ? -(x) : (x))

¿Por qué el segundo es correcto contra el primero?

¿Y por qué tienes que usar todos los (). ¿Como cuáles son las reglas involucradas? Cada variable necesita un ()? Gracias.


Sí, cada variable necesita paréntesis alrededor de ella directamente.

La razón es porque puedes pasar cosas a la macro que no son "agradables", como expresiones aritméticas o realmente cualquier expresión que no sea una sola variable. Debería ser fácil ver que con abs(1+2) , la expansión -(1 + 2) dará un resultado diferente a (-1 + 2) . Por eso -(x) es más correcto.

Desafortunadamente, ninguna de las macros es segura, debería usar una función en línea para cosas como esta. Considerar:

abs (x++); // expands to ((x++) < 0 ? - (x++) : (x++))

Esto está claramente mal con la macro, pero funcionaría correctamente si se utilizara una función en línea en su lugar.

Hay otros problemas con el uso de macros en lugar de funciones también. Vea esta pregunta .

Edición: teniendo en cuenta que la pregunta es sobre C, las funciones en línea pueden estar disponibles solo en C99.