¿Por qué el preprocesador C en GCC interpreta la palabra linux
(letras minúsculas) como la constante 1
?
prueba.c:
#include <stdio.h>
int main(void)
{
int linux = 5;
return 0;
}
Resultado de $ gcc -E test.c
(detener después de la etapa de preprocesamiento):
....
int main(void)
{
int 1 = 5;
return 0;
}
Lo que, por supuesto, produce un error.
(Por cierto: no hay #define linux
en el stdio.h
archivo).
c
linux
gcc
c-preprocessor
ahmedaly50
fuente
fuente
#undef linux
, o quizás usando una variable diferente? Creo que la constantelinux
se usa para probar sistemas operativos, por ejemplo, si está diseñando una aplicación multiplataforma y necesita saber exactamente qué API usar (Windows, Mac, Linux, BSD, etc.). No está en stdio.h, pero aún está definido si el núcleo es Linux. El mismo código no debería producir un error en Windows, pero usar algo como Windows o WINDOWS como probablemente lo hará la variable, y viceversaRespuestas:
En los viejos tiempos (pre-ANSI), predefinir símbolos como
unix
yvax
era una forma de permitir que el código detectara en el momento de la compilación para qué sistema se estaba compilando. No existía un estándar de idioma oficial en ese entonces (más allá del material de referencia en la parte posterior de la primera edición de K&R), y el código C de cualquier complejidad era típicamente un complejo laberinto de#ifdef
s para permitir diferencias entre sistemas. Estas definiciones de macro generalmente fueron establecidas por el compilador, no definido en un archivo de encabezado de biblioteca. Dado que no había reglas reales sobre qué identificadores podrían ser utilizados por la implementación y cuáles estaban reservados para los programadores, los escritores de compiladores se sintieron libres de usar nombres simples comounix
y asumieron que los programadores simplemente evitarían usar esos nombres para sus propios fines.El estándar ANSI C de 1989 introdujo reglas que restringen los símbolos que una implementación podría predefinir legalmente. Una macro predefinida por el compilador solo podría tener un nombre que comience con dos guiones bajos, o con un guión bajo seguido de una letra mayúscula, dejando a los programadores libres de usar identificadores que no coincidan con ese patrón y que no se usen en la biblioteca estándar.
Como resultado, cualquier compilador que predefine
unix
olinux
no sea conforme, ya que no podrá compilar código perfectamente legal que use algo asíint linux = 5;
.De hecho, gcc no es conforme de forma predeterminada, pero se puede hacer que se ajuste (razonablemente bien) con las opciones de línea de comandos correctas:
Consulte el manual de gcc para más detalles.
gcc eliminará estas definiciones en futuras versiones, por lo que no debe escribir código que dependa de ellas. Si su programa necesita saber si se está compilando para un objetivo de Linux o no, puede verificar si
__linux__
está definido (suponiendo que esté usando gcc o un compilador que sea compatible con él). Consulte el manual del preprocesador GNU C para obtener más información.Un aparte en gran medida irrelevante: el ganador del "Mejor trazador de líneas" del Concurso Internacional de Código C Ofuscado de 1987 , por David Korn (sí, el autor del Korn Shell) se aprovechó de la
unix
macro predefinida :Imprime
"unix"
, pero por razones que no tienen absolutamente nada que ver con la ortografía del nombre de la macro.fuente
unix
yvax
" - ¿Eh? Tenía entendido que en los viejos tiempos, ¡todo el mundo era unvax
!unix
definición). Le expliqué por qué en un comentario sobre esa esencia si usted, o alguien más, es curioso.Esto parece ser una "extensión GNU" (no documentada): [ corrección : finalmente encontré una mención en los documentos. Vea abajo.]
El siguiente comando usa la
-dM
opción para imprimir todas las definiciones de preprocesador; Como el "archivo" de entrada está vacío, muestra exactamente las macros predefinidas. Se ejecutó con gcc-4.7.3 en una instalación estándar de ubuntu. Puede ver que el preprocesador tiene en cuenta los estándares. En total, hay 243 macros con-std=gnu99
y 240 con-std=c99
; Filtré la salida por relevancia.Las versiones "gnu standard" también
#define unix
. (Usandoc11
ygnu11
produce los mismos resultados.)Supongo que tenían sus razones, pero me parece que la instalación predeterminada de gcc (que compila el código C con
-std=gnu89
menos que se especifique lo contrario) no es conforme y, como en esta pregunta, es sorprendente. Contaminar el espacio de nombres global con macros cuyos nombres no comienzan con un guión bajo no está permitido en una implementación conforme. (6.8.10p2: "Cualquier otro nombre de macro predefinido comenzará con un guión bajo seguido de una letra mayúscula o un segundo guión bajo", pero, como se menciona en el Apéndice J.5 (problemas de portabilidad), dichos nombres a menudo están predefinidos).Cuando originalmente escribí esta respuesta, no pude encontrar ninguna documentación en gcc sobre este problema, pero finalmente lo descubrí, no en el comportamiento definido por la implementación de C ni en las extensiones de C, sino en la
cpp
sección 3.7.3 del manual , donde nota que:fuente
-std=gnu89
es el predeterminado, y que yo sepa, es el predeterminado en Solaris, Linux y Mac OS X ( developer.apple.com/library/mac/documentation/Darwin/Reference/… para este último), y es el que contamina el espacio de nombres.c89
intenta cumplir con el estándar, por lo que si fuera el predeterminado no tendría una queja.Porque
linux
es una macro incorporada definida cuando el compilador se está ejecutando o compilando (si es un compilador cruzado), Linux.Hay muchas macros predefinidas. Con GCC, puede usar:
para obtener una lista de macros. (No he logrado persuadir a GCC para que acepte
/dev/null
directamente, pero el archivo vacío parece funcionar bien). Con GCC 4.8.1 ejecutándose en Mac OS X 10.8.5, obtuve el resultado:Eso es 236 macros de un archivo vacío. Cuando agregué
#include <stdio.h>
al archivo, el número de macros definidas subió a 505. Esto incluye todo tipo de macros de identificación de plataforma.fuente
cpp -dM < /dev/null
linux
está definido en mi máquina Mac OS X, no está ejecutando Linux (bueno, hay una VM que ejecuta Linux, pero ...). Puede estar definido por los encabezados incluidos por<stdio.h>
; ejecutar un archivo vacío puede no ser lo mismo./dev/null
como un archivo fuente,-x
ya que no tiene una extensión, por lo que gcc no sabe si es C o C ++. Puede usargcc -E -dM -x c /dev/null
ogcc -E -dm -x c++ /dev/null
para obtener la lista sin tener que crear un archivo vacío.cpp
(el preprocesador) nogcc
. Tenga en cuenta también que no le dan/dev/null
como un archivo de entrada, sino que estoy usando para hacer la redirección/dev/null
enstdin
. También @KerrekSB: en mi máquina Debianlinux
está en la lista.> emptyfile.c
o: > emptyfile.c
odd if=/etc/passwd of=emptyfile.c count=0
o ...De
info gcc
(énfasis mío):(Utiliza vax en el ejemplo en lugar de Linux porque cuando se escribió tal vez era más popular ;-).
La idea básica es que GCC solo intenta cumplir completamente con los estándares ISO cuando se invoca con la
-ansi
opción.fuente
-ansi
ostd=cXX
, dondeXX
es89
,90
,99
, o11
, y ya sea-pedantic
o-pedantic-errors
. Incluso eso es una simplificación excesiva; Vea el manual para más detalles.Usa este comando
para conseguir esto
fuente