2012-04-06 24 views
9

Soy un principiante en C, y yo estaba jugando con C. He escrito un código C de esta manera:¿Por qué este programa C se compila sin un error?

#include <stdio.h> 
int main() 
{ 
    printf("hello world\n"); 
    \ 
    return 0; 
} 

A pesar de que he usado \ a sabiendas, el compilador de C no lanza ningún error. ¿Para qué se usa este símbolo en el lenguaje C?

Editar:

Incluso esto funciona:

"\n"; 
+0

"\ n"; es una declaración sin ningún efecto. Es completamente ignorado por el compilador y genera una advertencia. Intente compilar con gcc -Wall flag que habilite las advertencias de compilación. – dAm2K

+3

Su pregunta principal ha sido respondida varias veces. En referencia a la parte sobre '" \ n ";', un programa c es (más o menos) una lista de enunciados. Un valor literal (como '3', o' "\ n" ', o' "hamburgers" ') es una afirmación perfectamente válida, aunque no _do_ nada. – jpm

Respuesta

0

La barra invertida \ va a interpretar por el preprocesador C. Protege su siguiente personaje (el nuevo personaje de línea en su caso).

+1

En realidad, es lo opuesto a "proteger"; asegura que tanto la barra invertida como la nueva línea se eliminen. –

0

La barra diagonal inversa simplemente está escapando al siguiente caracter. En este caso, probablemente un carácter de final de línea (CR). Perfectamente razonable.

+0

Los escapes en C están habilitados solo dentro de cadenas. –

+0

De hecho, el preprocesador solo podría escapar del carácter de fin de línea. Disculpas por engañar. La respuesta por dAm2k anterior es más precisa. – rainecc

+0

@MatteoItalia: La fase 2 de la traducción es: "Se elimina cada instancia de un carácter de barra diagonal inversa (\\) seguida inmediatamente por un carácter de nueva línea, empalmando líneas de origen físico para formar líneas de origen lógicas". (§5.1.1.2/1.1). –

11

La secuencia backslash-newline se elimina del código en una fase muy temprana (fase 2) del proceso de traducción. Solía ​​ser cómo se creaban los literales largos de cadena antes de que hubiera una concatenación de cadenas, y cómo se siguen ampliando las macros en varias líneas.

Ver §5.1.1.2 Fases de traducción de la norma C99:

La precedencia entre las reglas de sintaxis de la traducción está especificado por el fases siguientes. 5)

  1. personajes archivo de origen de varios bytes físicos se asignan, en una implementación definen manera, al conjunto de caracteres de origen (la introducción de caracteres de nueva línea de indicadores de fin de línea) si es necesario. Las secuencias de Trigraph se reemplazan por representaciones internas de un solo carácter correspondientes.
  2. Se elimina cada instancia de un carácter de barra diagonal inversa (\) seguido de una nueva línea , empalmando líneas de origen físico para formar líneas de origen lógicas. Solo la última barra invertida en cualquier línea de origen física será elegible para ser parte de dicho empalme. Un archivo de origen que no esté vacío finalizará en un carácter de nueva línea, , que no deberá ir precedido inmediatamente de una barra invertida antes de que se produzca dicho empalme .
  3. El archivo de origen se descompone en tokens de preprocesamiento 6) y secuencias de caracteres de espacio en blanco (incluidos los comentarios). Un archivo de origen no debe terminar en un token de preproceso parcial o en un comentario parcial. Cada comentario se reemplaza por un carácter de espacio. Los caracteres de nueva línea se conservan. Si cada secuencia no vacía de espacios en blanco que no sean de nueva línea se conserva o reemplaza por , un carácter de espacio está definido por la implementación.
  4. Se ejecutan las directivas de preprocesamiento, se expanden las macrovocaciones y se ejecutan las expresiones de operador unario _Pragma. Si una secuencia de caracteres que concuerda con la sintaxis de un nombre de carácter universal se produce mediante el token concatenación (6.10.3.3), el comportamiento no está definido.Una directiva de preprocesamiento #include de hace que el encabezado con nombre o el archivo de origen se procese desde la fase 1 hasta la fase 4, recursivamente. Todas las directivas de preprocesamiento se eliminan.
  5. Cada conjunto de caracteres de origen y secuencia de escape en constantes de caracteres y literales de cadena se convierten en el miembro correspondiente del carácter de ejecución establecer; si no hay un miembro correspondiente, se convierte en un miembro definido como implementación que no sea el carácter nulo (ancho). 7)
  6. Los tokens literales de cadena adyacente están concatenados.
  7. Los caracteres de espacio en blanco que separan los tokens ya no son significativos. Cada token de preproceso de se convierte en un token. Los tokens resultantes son analizados sintáctica y semánticamente y traducidos como una unidad de traducción.
  8. Se resuelven todas las referencias de objetos y funciones externas. Los componentes de biblioteca son vinculados para satisfacer las referencias externas a funciones y objetos no definidos en la traducción actual . Toda esa salida de traducción se recoge en una imagen de programa que contiene la información necesaria para la ejecución en su entorno de ejecución.

5) Implementaciones se comportan como si se producen estas fases separadas, a pesar de que muchos están típicamente pliegan juntas en la práctica.

6) Como se describe en 6.4, el proceso de dividir caracteres de un archivo fuente en tokens de preprocesamiento es dependiente del contexto. Por ejemplo, consulte el manejo de < dentro de una directiva de preprocesamiento #include.

7) Una implementación no necesita convertir todos los caracteres de origen no correspondientes al mismo carácter de ejecución .

Si tenía un espacio en blanco o cualquier otro carácter después de su barra invertida, tendría un error de compilación. Podemos decir que no tiene nada después porque no tiene un error de compilación.


La otra parte de su pregunta, sobre: ​​

"\n"; 

es bastante diferente. Es una expresión simple que no tiene efectos secundarios y, por lo tanto, no tiene ningún efecto en el programa. El optimizador lo descartará por completo. Cuando escriba:

i = 1; 

tiene una expresión con un valor que se descarta; se evalúa por su efecto secundario de modificar i.

A veces, encontrará un código como:

*ptr++; 

El compilador le advertirá de que el resultado de la expresión se descarta; la expresión se puede simplificar a:

ptr++; 

y logrará el mismo efecto en el programa.

+0

En la fase uno, los "indicadores de fin de línea" están definidos por la implementación. El espacio en blanco al final podría considerarse parte del indicador de fin de línea, por lo que en la fase 2, la barra invertida se seguiría inmediatamente a una línea nueva, aunque hubiera espacio en blanco en la entrada original. –

+0

@JerryCoffin: En teoría, estás en lo cierto. En la práctica, mi declaración simplificada se aplica a todos los entornos modernos de los que he oído hablar. ¿Tiene un contraejemplo explícito donde el compilador de C elimina los espacios en blanco finales antes de hacer el empalme backslash-newline? (Se citan las reglas exactas del estándar C99; cualquier comentario que haga ocupe el segundo lugar del estándar y los errores en la implementación que se está usando.) –

+0

No, no es el actual, aunque me parece recordar algún antiguo que lo hizo parte del tiempo –

0

La barra diagonal inversa más lo que sigue es escape sequence; "\ n" juntos es el carácter de nueva línea (imprime una nueva línea). Otra importante es "\ t", para la pestaña.

+0

Las secuencias de escape solo se producen en cadenas y caracteres literales. La barra diagonal inversa en la pregunta no está dentro de una cadena o literal de caracteres. –

4

El \, cuando es inmediatamente seguido de una nueva línea, es consumido por el preprocesamiento y hace que la siguiente línea "física" se una a la línea lógica actual. Esto es muy importante para la escritura de las directivas de preprocesamiento de largo, que tienen que ser todo en una línea lógica:

#define SHORT very log macro \ 
    consisting of lots and \ 
    lots of preprocessor \ 
    tokens 

Si elimina las secuencias de barra invertida-nueva línea, ya no es correcta. Algunos otros lenguajes de la cultura Unix tienen una sintaxis de continuación de línea diagonal inversa similar: el lenguaje de shell POSIX derivado del shell Bourne y también los archivos make.

$ this is \ 
one shell command 

Sobre "\n";, que es una expresión primaria utilizada para formar una expresión-comunicado. En C, las expresiones se pueden usar como declaraciones, y esto se explota todo el tiempo. Su llamada printf, por ejemplo, es una declaración de expresión. printf("hello world\n") es una expresión de postfijo que llama a una función, obteniendo un valor de retorno. Como utilizó esta expresión como una declaración, el valor de retorno se descarta. El valor de retorno de printf indica cuántos caracteres se imprimieron, o si fue exitoso en absoluto, por lo que al tirarlo, su programa ignora si la llamada printf realmente funcionó.

Dado que el valor de un enunciado de expresión se descarta, si dicho enunciado tampoco tiene efectos secundarios, es una afirmación inútil que no hace nada (como su "\n"). Pero tales declaraciones de expresiones inútiles no son erróneas. Si agrega opciones de advertencia a su línea de comandos del compilador, es posible que reciba una advertencia como "declaración sin efecto" o algo así.

Cuestiones relacionadas