macros - macro - define max en c
¿Por qué se reemplaza esta macro como 20 en lugar de 10? (4)
El estándar C11 dice (y otras versiones de C y C ++, dicen de manera similar):
Una directiva de preprocesamiento de la forma
# define identifier replacement-list new-line
define una macro similar a un objeto que hace que cada instancia posterior del nombre de macro sea reemplazada por la lista de reemplazo de tokens de preprocesamiento que constituyen el resto de la directiva. La lista de reemplazo se vuelve a explorar para obtener más nombres de macro como se especifica a continuación.
Sin embargo, también lo dice en otra parte (gracias a rici por señalar esto).
Los tokens de preprocesamiento dentro de una directiva de preprocesamiento no están sujetos a expansión macro a menos que se indique lo contrario.
Por lo tanto, una instancia posterior del nombre de macro
que se encuentra dentro de otra directiva
#define
realidad
no se
reemplaza.
Su línea
#define FOO NUM
define que cuando el token
FOO
se encuentre más tarde (fuera de otra directiva
#define
!), Será reemplazado por el token
NUM
.
Después de reemplazar un token, se produce un nuevo
escaneo
, y si
NUM
es en sí mismo una macro, entonces
NUM
se reemplaza en ese punto.
(Y si lo que
NUM
expande contiene macros, entonces eso se expande, y así sucesivamente).
Entonces su secuencia de pasos es en realidad:
-
NUM
definido como10
-
FOO
definido comoNUM
-
NUM
indefinido y redefinido como20
-
FOO
expande aNUM
-
(reescanear)
NUM
expande a20
Este comportamiento se puede ver en otro truco común del preprocesador, para convertir el valor definido de una macro en una cadena:
#define STR(X) #X
#define STR_MACRO(X) STR(X)
#define NUM 10
puts( STR_MACRO(NUM) ); // output: 10
Si hubiéramos escrito
puts( STR(NUM) )
, la salida sería
NUM
.
La salida de
10
es posible porque, como antes, el segundo
#define
aquí en realidad no expande
STR
.
Entonces, la secuencia de pasos en este código es:
-
STR(X)
definido como#X
-
STR_MACRO(X)
definido comoSTR(X)
-
NUM
definido como10
-
STR_MACRO
yNUM
están expandidos; el resultado esputs( STR(10) );
-
(Resultado de reescaneo de la última expansión)
STR(10)
se expande a"10"
- (Volver a analizar el resultado de la última expansión) No es posible una expansión adicional.
1. #define NUM 10
2. #define FOO NUM
3. #undef NUM
4. #define NUM 20
5.
6. FOO
Cuando solo ejecuto el preprocesador, el archivo de salida contiene 20.
Sin embargo, por lo que entiendo, el preprocesador simplemente reemplaza el texto. Entonces, esto es lo que creo que está sucediendo (lo cual obviamente es incorrecto pero idky):
- NUM se define como 10.
- Por lo tanto, en la línea 2, NUM se reemplaza como 10. Entonces ahora tenemos "#define FOO 10".
- NUM no está definido.
- NUM está redefinido y ahora es 20.
- FOO se reemplaza de acuerdo con la línea 2, que era antes de la redefinición de la línea 4, y es 10.
Así que creo que la salida debería ser 10 en lugar de 20. ¿Algo puede explicar dónde salió mal?
El reemplazo de texto se realiza donde se usa la macro, no donde escribió el
#define
.
En el momento en que usa
FOO
, reemplaza
FOO
con
NUM
y
NUM
se define actualmente como
20
.
En el interés de recopilar todas las especificaciones relevantes de los estándares, extraje esta información de un hilo de comentarios y agregué números de sección C ++, basados en el borrador N4527 (el texto normativo es idéntico en los dos estándares). Los estándares son absolutamente claros sobre el tema.
-
#define
directivas de preprocesador no se someten a reemplazo de macro.(C11 §6.10¶7; C ++ §16 [cpp] ¶6): los tokens de preprocesamiento dentro de una directiva de preprocesamiento no están sujetos a expansión macro a menos que se indique lo contrario.
-
Después de reemplazar una macro con su texto de reemplazo, el nuevo texto se vuelve a explorar. Los tokens de preprocesador en el reemplazo se expanden como macros si hay una definición de macro activa para el token en ese punto del programa.
(C11 §6.10.3¶9; C ++ §16.3 [cpp.replace] ¶9) Una directiva de preprocesamiento del formulario
# define identifier replacement-list new-line
define una macro similar a un objeto que hace que cada instancia posterior del nombre de la macro sea reemplazada por la lista de reemplazo de tokens de preprocesamiento que constituyen el resto de la directiva. La lista de reemplazo se vuelve a explorar para obtener más nombres de macro como se especifica a continuación.
-
Una definición de macro está activa desde la línea que sigue al
#define
hasta un#undef
para el nombre de la macro o al final del archivo.(C11 §6.10.3.5¶1; C ++ §16.3.5 [cpp.scope] ¶1) Una definición de macro dura (independientemente de la estructura del bloque) hasta que se encuentre una directiva
#undef
correspondiente o (si no se encuentra ninguna) hasta el final de la unidad de traducción de preprocesamiento. Las definiciones de macro no tienen importancia después de la fase de traducción 4.
Si miramos el programa:
#define NUM 10
#define FOO NUM
#undef NUM
#define NUM 20
FOO
vemos que la definición macro de
NUM
en la línea 1 dura exactamente hasta la línea 3. No hay texto reemplazable en esas líneas, por lo que la definición nunca se usa;
en consecuencia, el programa es efectivamente el mismo que:
#define FOO NUM
#define NUM 20
FOO
En este programa, en la tercera línea, hay una definición activa para
FOO
, con la lista de reemplazo
NUM
, y para
NUM
, con la lista de reemplazo
20
.
El
FOO
se reemplaza con su lista de reemplazo, convirtiéndolo en
NUM
, y luego se escanea una vez más en busca de macros, lo que resulta en
NUM
reemplazado por su lista de reemplazo 20. Ese reemplazo se vuelve a explorar, pero no hay macros definidas, por lo que el final El resultado es que el token
20
se deja para procesar en la fase de traducción 5.
En:
FOO
el preprocesador lo reemplazará con
NUM
, luego reemplazará
NUM
con lo que actualmente se define, que es
20
.
Esas cuatro líneas iniciales son equivalentes a:
#define FOO NUM
#define NUM 20