2010-07-19 46 views
6

¿Por qué estos bloques de código producen resultados diferentes?Intentando comprender el preprocesador C

Algunos código común:

#define PART1PART2 works 
#define STRINGAFY0(s) #s 
#define STRINGAFY1(s) STRINGAFY0(s) 

caso 1:

#define GLUE(a,b,c) a##b##c 
STRINGAFY1(GLUE(PART1,PART2,*)) 
//yields 
"PART1PART2*" 

caso 2:

#define GLUE(a,b) a##b##* 
STRINGAFY1(GLUE(PART1,PART2)) 
//yields 
"works*" 

caso 3:

#define GLUE(a,b) a##b 
STRINGAFY1(GLUE(PART1,PART2*)) 
//yields 
"PART1PART2*" 

estoy usando MSVC++ 2005 SP1 de VS.net

Editar: es actualmente mi creencia de que el preprocesador funciona así: cuando la expansión de macros Paso 1: - tome el cuerpo - eliminar cualquier espacio en blanco alrededor de ## operadores - analizar la cadena, en el caso de que se encuentre un identificador que coincida con el nombre de un parámetro: -si está al lado de un operador ##, reemplace el identificador con el valor literal del parámetro (es decir, la cadena pasó) -si NO está al lado de un operador ##, primero ejecute todo este proceso de explicación sobre el valor del parámetro, luego reemplace el identificador con ese resultado. (ignorando el single '#' atm caso stringafy) -remove todos los ## operadores

Paso 2: - tomar esa cadena resultante y analizarlo para cualquier macro

ahora, desde que yo creo que todos 3 casos deben producir exactamente la misma cadena resultante:

PART1PART2 *

y por lo tanto después de la etapa 2, debe resultar en

obras *

pero al menos debería dar como resultado lo mismo.

Respuesta

3

casos 1 y 2 no tienen un comportamiento definido ya que es tentador pegar un * en un token de preprocesador. De acuerdo con las reglas de asociación de su preprocesador, esto intenta unir los tokens PART1PART2 (o simplemente PART2) y *. En su caso, esto probablemente falla en silencio, que es uno de los posibles resultados cuando las cosas no están definidas. El token PART1PART2 seguido de * no se considerará para expansión de macros nuevamente. Stringfication produce el resultado que ves.

Mi gcc comporta de manera diferente en sus ejemplos:

/usr/bin/gcc -O0 -g -std=c89 -pedantic -E test-prepro.c 
test-prepro.c:16:1: error: pasting "PART1PART2" and "*" does not give a valid preprocessing token 
"works*" 

Entonces, para resumir su caso 1 tiene dos problemas.

  • pegar dos fichas que no resultan en una ficha preprocesador válida.
  • orden de evaluación de la ## operador

En el caso 3, el compilador está dando un resultado erróneo. Debe

  1. evaluar los argumentos a STRINGAFY1
  2. que hacer lo que tiene que ampliar GLUE
  3. GLUE resultados en PART1PART2*
  4. que debe ser ampliado una vez más
  5. el resultado es works*
  6. que luego pasa a STRINGAFY1
+0

no debería 1 y caso 2 resultan en lo mismo, ¿entonces? – matt

+0

¿qué es un "token de preprocesador válido"? tal vez un identificador? en ese caso, no veo cómo funcionaría ninguno de ellos. Supongo que el caso 2 - el único que funciona - es el único que pasa identificadores legales a todos los parámetros ... – matt

+0

@matt, ningún caso 1 simplemente no está definido, por lo que no puede saber qué es lo que el compilador elige para resolver ese problema . –

1

Está haciendo exactamente lo que le está diciendo que haga. El primero y el segundo toman los nombres de símbolos pasados ​​y los pegan juntos en un nuevo símbolo. El tercero toma 2 símbolos y los pega, luego coloca el * en la cadena usted mismo (que eventualmente lo evaluará en otra cosa)

¿Cuál es exactamente la pregunta con los resultados? ¿Qué esperabas obtener? Todo parece estar funcionando como yo esperaba.

Entonces, por supuesto, es la pregunta de por qué estás jugando con las artes oscuras del símbolo munging así de todos modos? :)

+0

hasta donde puedo decir, después de que el expansor de macro haya pegado/sustituido los parámetros en, y haya ordenado todos los operadores '##', los tres deberían producir exactamente la misma cadena, 'PART1PART2 *' como cómo se debe hacer esto antes de que el cuerpo expandido sea analizado para sub macros, esperaría el mismo resultado para todos 3 – matt

+0

oh sí, y solo lo estoy haciendo para tratar de entender exactamente cómo funciona el preprocesador, nunca escribiría horrible código como este :) – matt

+0

La construcción 'a ## b' no expande' a' y 'b', y' # c' no expande 'c'. Ver http://www.boost.org/doc/libs/1_43_0/libs/preprocessor/doc/ref/cat.html – Philipp

Cuestiones relacionadas