¿El preprocesador C elimina comentarios o expande macros primero?

Considere esta estructura de código (horrible, terrible, no bueno, muy malo):

#define foo(x) // commented out debugging code // Misformatted to not obscure the point if (a) foo(a); bar(a); 

He visto dos preprocesadores de comstackdores generar diferentes resultados en este código:

 if (a) bar(a); 

y

 if (a) ; bar(a); 

Obviamente, esto es algo malo para una base de código portátil.

Mi pregunta: ¿qué se supone que haga el preprocesador con esto? ¿Elide comenta primero o expande macros primero?

Desafortunadamente, la Especificación ANSI C original excluye específicamente cualquier característica del Preprocesador en la sección 4 (“Esta especificación describe solo el lenguaje C. No incluye ninguna provisión para la biblioteca o el preprocesador”).

La especificación C99 maneja esta explicidad, sin embargo. Los comentarios se reemplazan con un espacio único en la “fase de traducción”, que ocurre antes del análisis de la directiva de preprocesamiento. (Sección 6.10 para más detalles).

Tanto VC ++ como el Comstackdor C de GNU siguen este paradigma: es posible que otros comstackdores no sean compatibles si son más antiguos, pero si cumple con C99, debe estar seguro.

Como se describe en esta descripción copy-n-pegada de las fases de traducción en el estándar C99, la eliminación de comentarios (se reemplazan por un solo espacio en blanco) ocurre en la fase de traducción 3, mientras que las directivas de preprocesamiento se manejan y las macros se expanden en la fase 4.

En el estándar C90 (que solo tengo en copia impresa, así que no copie y pegue) estas dos fases ocurren en el mismo orden, aunque la descripción de las fases de traducción es ligeramente diferente en algunos detalles del estándar C99: el hecho que los comentarios se eliminen y reemplacen por un solo carácter de espacio en blanco antes de que se manejen las directivas de preprocesamiento y que las macros se expandan no es diferente.

De nuevo, el estándar de C ++ tiene estas 2 fases que ocurren en el mismo orden.

En cuanto a cómo deben manejarse los comentarios ‘ // ‘, el estándar C99 dice esto (6.4.9 / 2):

Excepto dentro de una constante de caracteres, un literal de cadena o un comentario, los caracteres // introducen un comentario que incluye todos los caracteres multibyte hasta, pero sin incluir, el siguiente carácter de nueva línea.

Y el estándar de C ++ dice (2.7):

Los personajes // comienzan un comentario, que finaliza con el siguiente carácter de nueva línea.

Entonces, su primer ejemplo es claramente un error por parte de ese traductor: el ‘ ; ‘character after the foo(a) debe conservarse cuando se expande la macro foo() – los caracteres de comentario no deben ser parte del’ contenido ‘de the foo() macro the foo() .

Pero dado que se enfrenta a un traductor de errores, es posible que desee cambiar la definición de macro a:

 #define foo(x) /* junk */ 

para solucionar el error.

Sin embargo (y me estoy desviando del tema aquí …), dado que el empalme de líneas (barras invertidas justo antes de una nueva línea) ocurre antes de que se procesen los comentarios, puede encontrarse con algo como este código desagradable:

 #define evil( x) printf( "hello "); // hi there, \ printf( "%s\n", x); // you! int main( int argc, char** argv) { evil( "bastard"); return 0; } 

Lo cual podría sorprender a quien lo haya escrito.

O mejor aún, intente lo siguiente, escrito por alguien (¡ciertamente no yo!) A quien le gustan los comentarios estilo caja:

 int main( int argc, char** argv) { //----------------/ printf( "hello "); // Hey, what the??/ printf( "%s\n", "you"); // heck?? / //----------------/ return 0; } 

Dependiendo de si su comstackdor usa trigrafos de procesamiento o no (se supone que los comstackdores lo hagan, pero dado que los trigrafos sorprenden a casi todos los que se cruzan con ellos, algunos comstackdores deciden desactivarlos por defecto), puede obtener o no el comportamiento que desea: cualquier comportamiento que sea, por supuesto.

Según MSDN , los comentarios se reemplazan por un único espacio en la fase de tokenización, que ocurre antes de la fase de preprocesamiento donde se expanden las macros.

Nunca ponga // comentarios en sus macros. Si debe poner comentarios, use / * * /. Además, tienes un error en tu macro:

 #define foo(x) do { } while(0) /* junk */ 

De esta forma, Foo siempre es seguro de usar. Por ejemplo:

 if (some condition) foo(x); 

nunca lanzará un error de comstackción independientemente de si foo está definido o no para alguna expresión.

 #ifdef _TEST_ #define _cerr cerr #else #define _cerr / ## / cerr #endif 
  • funcionará en algunos comstackdores (VC ++). Cuando _TEST_ no está definido,

    _cerr …

    será reemplazado por la línea de comentarios

    // cerr …

Me parece recordar que el cumplimiento requiere tres pasos:

  1. tira
  2. ampliar macros
  3. tirar de nuevo

La razón de esto tiene que ver con que el comstackdor pueda aceptar archivos .i directamente.