Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

305
Views
Comprender el comportamiento del preprocesador de C cuando una macro se expande indirectamente

Mientras trabajaba en un gran proyecto lleno de trucos y hechicería con macros, me topé con un error en el que una macro no se expandía correctamente. La salida resultante fue " EXPAND(0) ", pero EXPAND se definió como " #define EXPAND(X) X ", por lo que claramente la salida debería haber sido " 0 ".

"No hay problema", pensé para mis adentros. "Probablemente sea un error tonto, hay algunas macros desagradables aquí, después de todo, muchos lugares pueden salir mal". Mientras pensaba eso, aislé las macros que se comportaban mal en su propio proyecto, unas 200 líneas, y comencé a trabajar en un MWE para identificar el problema. 200 líneas se convirtieron en 150, que a su vez se convirtieron en 100, luego en 20, 10... Para mi absoluta sorpresa, este fue mi MWE final:

 #define EXPAND(X) X #define PARENTHESIS() () #define TEST() EXPAND(0) EXPAND(TEST PARENTHESIS()) // EXPAND(0)

4 lineas

Para colmo de males, casi cualquier modificación a las macros hará que funcionen correctamente:

 #define EXPAND(X) X #define PARENTHESIS() () #define TEST() EXPAND(0) // Manually replaced PARENTHESIS() EXPAND(TEST ()) // 0
 #define EXPAND(X) X #define PARENTHESIS() () #define TEST() EXPAND(0) // Manually replaced TEST() EXPAND(EXPAND(0)) // 0
 // Set EXPAND to 0 instead of X #define EXPAND(X) 0 #define PARENTHESIS() () #define TEST() EXPAND(0) EXPAND(TEST PARENTHESIS()) // 0

Pero lo más importante, y lo más extraño, el siguiente código falla exactamente de la misma manera:

 #define EXPAND(X) X #define PARENTHESIS() () #define TEST() EXPAND(0) EXPAND(EXPAND(EXPAND(EXPAND(TEST PARENTHESIS())))) // EXPAND(0)

Esto significa que el preprocesador es perfectamente capaz de expandir EXPAND , pero por alguna razón, se niega rotundamente a expandirlo nuevamente en el último paso.

Ahora, cómo voy a resolver este problema en mi programa real no está ni aquí ni allá. Aunque una solución sería buena (es decir, una forma de expandir el token EXPAND(TEST PARENTHESIS()) a 0 ), lo que más me interesa es: ¿por qué? ¿Por qué el preprocesador de C llegó a la conclusión de que " EXPAND(0) " era la expansión correcta en el primer caso, pero no en los otros?

Aunque es fácil encontrar recursos sobre lo que hace el preprocesador C (y algo de magia que puedes hacer con él), todavía tengo que encontrar uno que explique cómo lo hace, y quiero aprovechar esta oportunidad para entender mejor cómo funciona el preprocesador hace su trabajo y qué reglas usa al expandir macros.

Entonces, a la luz de eso: ¿Cuál es el razonamiento detrás de la decisión del preprocesador de expandir la macro final a " EXPAND(0) " en lugar de " 0 "?


Editar: después de leer la respuesta muy detallada, lógica y bien expresada de Chris Dodd, hice lo que cualquiera haría en la misma situación ... intente encontrar un contraejemplo :)

Lo que inventé fue este diferente de 4 líneas:

 #define EXPAND(X) X #define GLUE(X,Y) XY #define MACRO() GLUE(A,B) EXPAND(GLUE(MACRO, ())) // GLUE(A,B)

Ahora, sabiendo el hecho de que el preprocesador C no está completo en Turing , no hay forma de que lo anterior se expanda a AB . Si ese fuera el caso, GLUE expandiría MACRO y MACRO expandiría GLUE . Eso conduciría a la posibilidad de una recursividad ilimitada, lo que probablemente implicaría la Completitud de Turing para el Cpp. Lamentablemente para los magos del preprocesador, la macro anterior que no se expande es una garantía.

Que fallar no es realmente el problema, el verdadero problema es: ¿Dónde? ¿Dónde decidió el preprocesador detener la expansión?

Analizando los pasos:

  • el paso 1 ve la macro EXPAND y escanea en la lista de argumentos GLUE(MACRO, ()) para X
  • el paso 2 reconoce GLUE(MACRO, ()) como una macro:
    • el paso 1 (anidado) obtiene MACRO y () como argumentos
    • el paso 2 los escanea pero no encuentra ninguna macro
    • el paso 3 se inserta en el cuerpo de la macro produciendo: MACRO ()
    • el paso 4 suprime GLUE y escanea MACRO () en busca de macros, encontrando MACRO
      • el paso 1 (anidado) obtiene una secuencia de token vacía para el argumento
      • el paso 2 escanea esa secuencia vacía y no hace nada
      • el paso 3 se inserta en el cuerpo de la macro GLUE(A,B)
      • el paso 4 escanea GLUE(A,B) en busca de macros y encuentra GLUE . Sin embargo, se suprime, por lo que se va como está.
  • por lo que el valor final de X después del paso 2 es GLUE(A,B) (observe que, dado que no estamos en el paso 4 de GLUE , en teoría, ya no se suprime)
  • el paso 3 lo inserta en el cuerpo, dando GLUE(A,B)
  • el paso 4 suprime EXPAND y escanea GLUE(A,B) en busca de más macros, encontrando GLUE ( uuh )
    • el paso 1 obtiene A y B para los argumentos ( oh no )
    • el paso 2 no hace nada con ellos
    • paso 3 sustituye en el cuerpo dando AB ( bueno... )
    • el paso 4 escanea AB en busca de macros, pero no encuentra nada
  • el resultado final es entonces AB

Cuál sería nuestro sueño. Lamentablemente, la macro se expande a GLUE(A,B) .

Así que nuestra pregunta es: ¿Por qué?

over 4 years ago · Santiago Trujillo
1 answers
Answer question

0

A los efectos de esta situación, hay tres pasos relevantes en el reemplazo de macros:

  1. Realice el reemplazo de macros en los argumentos.
  2. Reemplace la macro con su definición, con parámetros reemplazados con argumentos.
  3. Vuelva a escanear el resultado para un reemplazo adicional mientras suprime el nombre de la macro reemplazada.

En EXPAND(TEST PARENTHESIS()) :

  • Paso 1, el reemplazo de macros se realiza en el argumento EXPAND , TEST PARENTHESIS() :
    • TEST no va seguido de paréntesis, por lo que no se interpreta como una invocación de macro.
    • PARENTHESIS() es una invocación de macro, por lo que se realizan los tres pasos: Los argumentos están vacíos, por lo que no hay procesamiento para ellos. Luego PARENTHESIS() se reemplaza por () . Luego () se vuelve a escanear y no se encuentran macros.
    • El paso 1 está hecho y tenemos EXPAND(TEST ()) . ( TEST () no se vuelve a escanear porque no fue el resultado de ningún reemplazo de macro).
  • Paso 2, EXPAND(TEST ()) se reemplaza por TEST () .
  • Paso 3, TEST () se vuelve a escanear mientras se suprime EXPAND :
    • Paso 1, los argumentos están vacíos, por lo que no hay procesamiento para ellos.
    • Paso 2, TEST () se reemplaza por EXPAND(0) .
    • Paso 3, se EXPAND(0) , pero se suprime EXPAND .

En EXPAND(TEST ()) :

  • Paso 1, el reemplazo de macros se realiza en el argumento EXPAND :
    • Paso 1, los argumentos de TEST están vacíos, por lo que no hay procesamiento.
    • Paso 2, TEST () se reemplaza por EXPAND(0) .
    • Paso 3, este reemplazo se vuelve a escanear y EXPAND(0) se reemplaza por 0 .
  • Paso 2, EXPAND(TEST ()) se ha convertido en EXPAND(0) y EXPAND(0) se reemplaza por 0 .
  • El paso 3, 0 se vuelve a escanear en busca de más macros, pero no hay ninguna.

Los otros ejemplos en la pregunta siguen de manera similar. Se reduce a:

  • En TEST PARENTHESIS() , la falta de paréntesis después de TEST da como resultado que no se expanda mientras se procesan argumentos para una invocación de macro adjunta.
  • Los paréntesis se colocan después cuando se expande PARENTHESIS , pero esto es después de escanear TEST y no se vuelve a escanear durante el procesamiento del argumento.
  • Después de reemplazar la macro adjunta, se vuelve a escanear TEST y luego se reemplaza, pero, en este momento, se suprime el nombre de la macro adjunta.
over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!