GNU/Linux >> LINUX-Kenntnisse >  >> Linux

Warum interpretiert der C-Präprozessor das Wort Linux als Konstante 1?

Dies scheint eine (undokumentierte) "GNU-Erweiterung" zu sein:[Korrektur :Ich habe endlich eine Erwähnung in den Dokumenten gefunden. Siehe unten.]

Der folgende Befehl verwendet den -dM Option zum Drucken aller Präprozessordefinitionen; Da die Eingabe "Datei" leer ist, zeigt sie genau die vordefinierten Makros. Es wurde mit gcc-4.7.3 auf einer Standard-Ubuntu-Installation ausgeführt. Sie können sehen, dass der Präprozessor standardbewusst ist. Insgesamt gibt es 243 Makros mit -std=gnu99 und 240 mit -std=c99; Ich habe die Ausgabe nach Relevanz gefiltert.

$ 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

Die "GNU-Standard"-Versionen auch #define unix . (Mit c11 und gnu11 erzeugt die gleichen Ergebnisse.)

Ich nehme an, sie hatten ihre Gründe, aber es scheint mir, dass die Standardinstallation von gcc (die C-Code mit -std=gnu89 kompiliert sofern nicht anders angegeben) nicht konform und – wie in dieser Frage – überraschend. Das Verschmutzen des globalen Namensraums mit Makros, deren Namen nicht mit einem Unterstrich beginnen, ist in einer konformen Implementierung nicht erlaubt. (6.8.10p2:„Alle anderen vordefinierten Makronamen müssen mit einem führenden Unterstrich beginnen, gefolgt von einem Großbuchstaben oder einem zweiten Unterstrich“, aber wie in Anhang J.5 (Portabilitätsprobleme) erwähnt, sind solche Namen oft vordefiniert.)

Als ich diese Antwort ursprünglich schrieb, konnte ich in gcc keine Dokumentation zu diesem Problem finden, aber ich habe es schließlich entdeckt, weder im durch die C-Implementierung definierten Verhalten noch in C-Erweiterungen, sondern im cpp Abschnitt 3.7.3 des Handbuchs, wo Folgendes vermerkt ist:

Wir stellen alle vordefinierten Makros, die außerhalb des reservierten Namensraums liegen, langsam aus. Sie sollten sie niemals in neuen Programmen verwenden…


Früher (vor ANSI) wurden Symbole wie unix vordefiniert und vax war eine Möglichkeit, Code zur Kompilierzeit zu erkennen, für welches System er kompiliert wurde. Damals gab es keinen offiziellen Sprachstandard (außer dem Referenzmaterial am Ende der ersten Ausgabe von K&R), und C-Code jeglicher Komplexität war typischerweise ein komplexes Labyrinth von #ifdef s um Unterschiede zwischen Systemen zu berücksichtigen. Diese Makrodefinitionen wurden im Allgemeinen vom Compiler selbst festgelegt und nicht in einer Bibliotheksheaderdatei definiert. Da es keine wirklichen Regeln darüber gab, welche Bezeichner von der Implementierung verwendet werden konnten und welche Programmierern vorbehalten waren, fühlten sich Compiler-Autoren frei, einfache Namen wie unix zu verwenden und davon ausgegangen, dass Programmierer es einfach vermeiden würden, diese Namen für ihre eigenen Zwecke zu verwenden.

Der ANSI C-Standard von 1989 führte Regeln ein, die einschränken, welche Symbole eine Implementierung legal vordefinieren kann. Ein vom Compiler vordefiniertes Makro konnte nur einen Namen haben, der mit zwei Unterstrichen oder einem Unterstrich gefolgt von einem Großbuchstaben beginnt, was Programmierern die Möglichkeit gibt, Bezeichner zu verwenden, die nicht diesem Muster entsprechen und nicht in der Standardbibliothek verwendet werden.

Als Ergebnis wird jeder Compiler, der unix vordefiniert oder linux ist nicht konform, da es nicht vollständig legalen Code kompilieren kann, der so etwas wie int linux = 5; verwendet .

Zufälligerweise ist gcc standardmäßig nicht konform – aber es kann (ziemlich gut) mit den richtigen Befehlszeilenoptionen konform gemacht werden:

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

Weitere Einzelheiten finden Sie im gcc-Handbuch.

gcc wird diese Definitionen in zukünftigen Versionen auslaufen lassen, daher sollten Sie keinen Code schreiben, der von ihnen abhängt. Wenn Ihr Programm wissen muss, ob es für ein Linux-Ziel kompiliert wird oder nicht, kann es prüfen, ob __linux__ definiert ist (vorausgesetzt, Sie verwenden gcc oder einen damit kompatiblen Compiler). Weitere Informationen finden Sie im GNU C-Präprozessor-Handbuch.

Eine weitgehend irrelevante Randbemerkung:Der Gewinner des „Best One Liner“ des International Obfuscated C Code Contest von 1987 von David Korn (ja, der Autor der Korn Shell) nutzte den vordefinierten unix Makro:

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

Es gibt "unix" aus , aber aus Gründen, die absolut nichts mit der Schreibweise des Makronamens zu tun haben.


Linux
  1. Warum ich von Mac zu Linux gewechselt bin

  2. Meine Linux-Geschichte:Warum Menschen den Raspberry Pi vorstellen

  3. Linux – Warum wird das Root-Verzeichnis mit einem / -Zeichen gekennzeichnet?

  4. Linux – Warum funktioniert Setuid nicht?

  5. Linux – Wie überprüft man die Verzeichnisstrukturinformationen einer Unix/Linux-Datei?

Warum den Pantheon-Desktop für Linux Elementary OS verwenden

Was ist die Linux-Prozesstabelle? Woraus besteht es?

Im Linux-Kernel 2.6.26 fand ich #define atomic_read(v) ((v)->counter + 0), warum +0?

Warum geht die Ausgabe einiger Linux-Programme weder an STDOUT noch an STDERR?

Warum heizt Linux meinen Computer auf?

Was bedeutet das Suffix .d unter Linux?