GNU/Linux >> Linux Esercitazione >  >> Linux

Perché il preprocessore C interpreta la parola linux come la costante 1?

Questa sembra essere una "estensione GNU" (non documentata):[correzione :ho finalmente trovato una menzione nei documenti. Vedi sotto.]

Il seguente comando usa -dM opzione per stampare tutte le definizioni del preprocessore; poiché il "file" di input è vuoto, mostra esattamente le macro predefinite. È stato eseguito con gcc-4.7.3 su un'installazione Ubuntu standard. Puoi vedere che il preprocessore è a conoscenza dello standard. In totale, ci sono 243 macro con -std=gnu99 e 240 con -std=c99; Ho filtrato l'output per pertinenza.

$ 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

Anche le versioni "gnu standard" #define unix . (Usando c11 e gnu11 produce gli stessi risultati.)

Suppongo che avessero le loro ragioni, ma mi sembra che facciano l'installazione predefinita di gcc (che compila il codice C con -std=gnu89 se non diversamente specificato) non conforme e, come in questa domanda, sorprendente. Inquinare lo spazio dei nomi globale con macro i cui nomi non iniziano con un carattere di sottolineatura non è consentito in un'implementazione conforme. (6.8.10p2:"Qualsiasi altro nome di macro predefinito deve iniziare con un trattino basso iniziale seguito da una lettera maiuscola o da un secondo trattino basso", ma, come menzionato nell'Appendice J.5 (problemi di portabilità), tali nomi sono spesso predefiniti.)

Quando ho originariamente scritto questa risposta, non sono riuscito a trovare alcuna documentazione in gcc su questo problema, ma alla fine l'ho scoperto, non nel comportamento definito dall'implementazione C né nelle estensioni C ma nel cpp sezione 3.7.3 del manuale, dove si nota che:

Stiamo lentamente eliminando tutte le macro predefinite che si trovano al di fuori dello spazio dei nomi riservato. Non dovresti mai usarli in nuovi programmi…


Ai vecchi tempi (pre-ANSI), simboli predefiniti come unix e vax era un modo per consentire al codice di rilevare in fase di compilazione per quale sistema veniva compilato. All'epoca non esisteva uno standard linguistico ufficiale (oltre al materiale di riferimento sul retro della prima edizione di K&R), e il codice C di qualsiasi complessità era tipicamente un complesso labirinto di #ifdef s per consentire le differenze tra i sistemi. Queste definizioni di macro erano generalmente impostate dal compilatore stesso, non definite in un file di intestazione della libreria. Poiché non c'erano regole reali su quali identificatori potevano essere usati dall'implementazione e quali erano riservati ai programmatori, gli scrittori di compilatori si sentivano liberi di usare nomi semplici come unix e presumeva che i programmatori avrebbero semplicemente evitato di utilizzare quei nomi per i propri scopi.

Lo standard ANSI C del 1989 ha introdotto regole che limitano i simboli che un'implementazione potrebbe legalmente predefinire. Una macro predefinita dal compilatore può avere solo un nome che inizia con due underscore, o con un underscore seguito da una lettera maiuscola, lasciando i programmatori liberi di usare identificatori che non corrispondono a quel pattern e non sono usati nella libreria standard.

Di conseguenza, qualsiasi compilatore che predefinisce unix o linux non è conforme, poiché non riuscirà a compilare un codice perfettamente legale che utilizza qualcosa come int linux = 5; .

Come accade, gcc è non conforme per impostazione predefinita, ma può essere reso conforme (ragionevolmente bene) con le giuste opzioni della riga di comando:

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

Consulta il manuale di gcc per maggiori dettagli.

gcc eliminerà gradualmente queste definizioni nelle versioni future, quindi non dovresti scrivere codice che dipende da esse. Se il tuo programma ha bisogno di sapere se è stato compilato per un target Linux o meno, può controllare se __linux__ è definito (supponendo che tu stia usando gcc o un compilatore compatibile con esso). Consulta il manuale del preprocessore GNU C per maggiori informazioni.

Una parentesi in gran parte irrilevante:il vincitore del "Best One Liner" dell'International Obfuscated C Code Contest del 1987, di David Korn (sì, l'autore del Korn Shell) ha approfittato del predefinito unix macro:

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

Stampa "unix" , ma per ragioni che non hanno assolutamente nulla a che fare con l'ortografia del nome della macro.


Linux
  1. Perché sono passato da Mac a Linux

  2. La mia storia su Linux:perché presentare alle persone il Raspberry Pi

  3. Perché l'utente root ha bisogno dell'autorizzazione Sudo?

  4. Linux:perché la directory principale è indicata con un segno /?

  5. Linux:perché ci vuole così tanto tempo per rilevare una chiavetta USB?

Perché usare il desktop Pantheon per Linux Elementary OS

Perché un Lun World Wide ID inizia con il numero 3 in Linux dm-multipath

Qual è la tabella dei processi Linux? In cosa consiste?

Nel kernel linux 2.6.26, ho trovato #define atomic_read(v) ((v)->counter + 0), perché +0?

Perché Ctrl + V non incolla in Bash (shell Linux)?

Perché l'output di alcuni programmi Linux non va né a STDOUT né a STDERR?