GNU/Linux >> Tutoriales Linux >  >> Linux

¿Por qué el preprocesador de C interpreta la palabra linux como la constante 1?

Esto parece ser una "extensión GNU" (no documentada):[corrección :finalmente encontré una mención en los documentos. Ver más abajo.]

El siguiente comando usa el -dM opción para imprimir todas las definiciones del preprocesador; dado que 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.

$ cpp --std=c89 -dM < /dev/null | grep linux
#define __linux 1
#define __linux__ 1
#define __gnu_linux__ 1

$ cpp --std=gnu89 -dM < /dev/null | grep linux
#define __linux 1
#define __linux__ 1
#define __gnu_linux__ 1
#define linux 1

$ cpp --std=c99 -dM < /dev/null | grep linux
#define __linux 1
#define __linux__ 1
#define __gnu_linux__ 1

$ cpp --std=gnu99 -dM < /dev/null | grep linux
#define __linux 1
#define __linux__ 1
#define __gnu_linux__ 1
#define linux 1

Las versiones "estándar gnu" también #define unix . (Usando c11 y gnu11 produce los mismos resultados.)

Supongo que tenían sus razones, pero me parece que hacer la instalación por defecto de gcc (que compila código C con -std=gnu89 a menos que se especifique lo contrario) no conforme y, como en esta pregunta, sorprendente. No se permite contaminar el espacio de nombres global con macros cuyos nombres no comienzan con un guión bajo en una implementación conforme. (6.8.10p2:"Cualquier otro nombre de macro predefinido debe 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 el cpp sección 3.7.3 del manual, donde se señala que:

Estamos eliminando gradualmente todas las macros predefinidas que están fuera del espacio de nombres reservado. Nunca debes usarlos en programas nuevos...


En los viejos tiempos (pre-ANSI), símbolos predefinidos como unix y vax era una forma de permitir que el código detectara en tiempo de compilación para qué sistema se estaba compilando. No habí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 propio compilador, no definidas en un archivo de encabezado de biblioteca. Dado que no había reglas reales sobre qué identificadores podía usar la implementación y cuáles estaban reservados para los programadores, los escritores de compiladores se sintieron libres de usar nombres simples como unix y asumió que los programadores simplemente evitarían usar esos nombres para sus propios fines.

El estándar ANSI C de 1989 introdujo reglas que restringían qué símbolos podía predefinir legalmente una implementación. Una macro predefinida por el compilador solo podía tener un nombre que comenzara con dos guiones bajos, o con un guión bajo seguido de una letra mayúscula, lo que dejaba a los programadores libres para usar identificadores que no coincidieran con ese patrón y que no se usaran en la biblioteca estándar.

Como resultado, cualquier compilador que predefina unix o linux no es conforme, ya que no podrá compilar un código perfectamente legal que use algo como int linux = 5; .

Da la casualidad de que gcc no se ajusta de forma predeterminada, pero se puede hacer que se ajuste (razonablemente bien) con las opciones correctas de la línea de comandos:

gcc -std=c90 -pedantic ... # or -std=c89 or -ansi
gcc -std=c99 -pedantic
gcc -std=c11 -pedantic

Consulte el manual de gcc para obtener más detalles.

gcc eliminará gradualmente estas definiciones en futuras versiones, por lo que no debe escribir código que dependa de ellas. Si su programa necesita saber si está siendo compilado para un destino 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 parte irrelevante:el ganador de "Best One Liner" del Concurso internacional de código C ofuscado de 1987, de David Korn (sí, el autor de Korn Shell) aprovechó el unix predefinido macro:

main() { printf(&unix["\021%six\012\0"],(unix)["have"]+"fun"-0x60);}

Imprime "unix" , pero por razones que no tienen absolutamente nada que ver con la ortografía del nombre de la macro.


Linux
  1. ¿Por qué el usuario raíz necesita permiso de Sudo?

  2. Linux:¿por qué el directorio raíz se indica con un signo /?

  3. Linux:¿por qué se tarda tanto en detectar una memoria USB?

  4. Linux:¿por qué el kernel no puede ejecutar Init?

  5. En el kernel de Linux 2.6.26, encontré #define atomic_read(v) ((v)->counter + 0), ¿por qué +0?

Por qué usar el escritorio Pantheon para Linux Elementary OS

¿Cómo funciona el comando 'ls' en Linux/Unix?

¿Qué es la tabla de procesos de Linux? ¿En qué consiste?

¿Qué significa &al final de un comando de Linux?

¿Por qué Ctrl + V no se pega en Bash (shell de Linux)?

¿Por qué Linux calienta mi computadora?