programa - ¿Cómo maneja el preprocesador C las dependencias circulares?
funcion preprocesador en c++ (5)
Aquí hay una buena demostración del comportamiento descrito en las rici''s y Eric Lippert , es decir, que un nombre de macro no se vuelve a expandir si se vuelve a encontrar al mismo tiempo que se expande la misma macro.
Contenido de test.c
:
#define ONE 1, TWO
#define TWO 2, THREE
#define THREE 3, ONE
int foo[] = {
ONE,
TWO,
THREE
};
Salida de gcc -E test.c
(excluyendo las líneas iniciales # 1 ...
):
int foo[] = {
1, 2, 3, ONE,
2, 3, 1, TWO,
3, 1, 2, THREE
};
(Publicaría esto como un comentario, pero incluir bloques de código sustanciales en los comentarios es algo incómodo, por lo que haré de esto una respuesta de Wiki comunitario. Si crees que sería mejor incluirla como parte de una respuesta existente, no dudes en para copiarlo y pedirme que elimine esta versión de CW).
Quiero saber cómo el preprocesador C maneja las dependencias circulares (de #defines). Este es mi programa:
#define ONE TWO
#define TWO THREE
#define THREE ONE
int main()
{
int ONE, TWO, THREE;
ONE = 1;
TWO = 2;
THREE = 3;
printf ("ONE, TWO, THREE = %d, %d, %d /n",ONE, TWO, THREE);
}
Aquí está la salida del preprocesador. No puedo entender por qué la salida es como tal. Me gustaría saber los diversos pasos que un preprocesador lleva en este caso para dar el siguiente resultado.
# 1 "check_macro.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "check_macro.c"
int main()
{
int ONE, TWO, THREE;
ONE = 1;
TWO = 2;
THREE = 3;
printf ("ONE, TWO, THREE = %d, %d, %d /n",ONE, TWO, THREE);
}
Estoy ejecutando este programa en linux 3.2.0-49-generic-pae y compilando en gcc versión 4.6.3 (Ubuntu / Linaro 4.6.3-1ubuntu5).
En su ejemplo, usted hace el macroproceso antes de definir las variables del mismo nombre, por lo tanto, independientemente de cuál sea el resultado del macroprocesamiento, ¡siempre imprime 1, 2, 3
!
Aquí hay un ejemplo donde las variables se definen primero:
#include <stdio.h>
int main()
{
int A = 1, B = 2, C = 3;
#define A B
#define B C
//#define C A
printf("%d/n", A);
printf("%d/n", B);
printf("%d/n", C);
}
Esto imprime 3 3 3
. De manera algo insidiosa, sin comentar #define CA
cambia el comportamiento de la línea printf("%d/n", B);
Mientras se amplía una macro de preprocesador, el nombre de esa macro no se expande. Entonces, los tres símbolos se definen como ellos mismos:
ONE -> TWO -> THREE -> ONE (not expanded because expansion of ONE is in progress)
TWO -> THREE -> ONE -> TWO ( " TWO " )
THREE -> ONE -> TWO -> THREE ( " THREE " )
Este comportamiento se establece en §6.10.3.4 del estándar C (número de sección del borrador C11, aunque hasta donde yo sé, la redacción y numeración de la sección no ha cambiado desde C89). Cuando se encuentra un nombre de macro, se reemplaza con su definición (y se tratan los operadores del preprocesador #
y ##
, así como los parámetros de macros similares a funciones). Luego, el resultado se vuelve a analizar para obtener más macros (en el contexto del resto del archivo):
2 / Si se encuentra el nombre de la macro que se reemplaza durante este escaneo de la lista de reemplazo (sin incluir el resto de los tokens de preprocesamiento del archivo de origen), no se reemplaza. Además, si alguno de los reemplazos anidados encuentra el nombre de la macro que se reemplaza, no se reemplaza ...
La cláusula continúa diciendo que cualquier token que no se reemplaza debido a una llamada recursiva está efectivamente "congelado": nunca será reemplazado:
... Estos tokens de preprocesamiento de nombres de macro no reemplazados ya no están disponibles para un reemplazo adicional, incluso si se examinan (re) más tarde en contextos en los que ese token de preprocesamiento de nombre de macro habría sido reemplazado.
La situación a la que se refiere la última frase rara vez aparece en la práctica, pero este es el caso más simple que pude pensar:
#define two one,two
#define a(x) b(x)
#define b(x,y) x,y
a(two)
El resultado es one, two
. two
se expande a one,two
durante el reemplazo de a
, y los two
expandidos se marcan como completamente expandidos. Posteriormente, b(one,two)
se expande. Esto ya no está en el contexto del reemplazo de two
, pero los two
que es el segundo argumento de b
se han congelado, por lo que no se vuelven a expandir.
Su pregunta es respondida por la publicación ISO / IEC 9899: TC2, sección 6.10.3.4 "Reescaneo y sustitución adicional", párrafo 2, que cito aquí para su conveniencia; en el futuro, considere leer la especificación cuando tenga una pregunta sobre la especificación .
Si se encuentra el nombre de la macro que se reemplaza durante este escaneo de la lista de reemplazo (sin incluir el resto de los tokens de preprocesamiento del archivo fuente), no se reemplaza. Además, si alguno de los reemplazos anidados encuentra el nombre de la macro que se reemplaza, no se reemplaza. Estos tokens de preprocesamiento de nombres de macro no reemplazados ya no están disponibles para un reemplazo adicional, incluso si luego se (re) examinan en contextos en los que ese token de preprocesamiento de nombre de macro hubiera sido reemplazado de otra manera.
https://gcc.gnu.org/onlinedocs/cpp/Self-Referential-Macros.html#Self-Referential-Macros responde la pregunta sobre las macros autorreferenciales.
El quid de la respuesta es que cuando el pre-procesador encuentra macros autorreferenciales, no los expande en absoluto.
Sospecho que la misma lógica se usa para evitar la expansión de macros definidas circularmente. De lo contrario, el preprocesador estará en una expansión infinita.