Warum interpretiert der C- Präprozessor in GCC das Wort linux
(Kleinbuchstaben) als Konstante 1
?
test.c:
#include <stdio.h>
int main(void)
{
int linux = 5;
return 0;
}
Ergebnis von $ gcc -E test.c
(Stopp nach der Vorverarbeitungsphase):
....
int main(void)
{
int 1 = 5;
return 0;
}
Was natürlich einen Fehler ergibt.
(Übrigens: #define linux
Die stdio.h
Datei enthält keine .)
c
linux
gcc
c-preprocessor
ahmedaly50
quelle
quelle
#undef linux
oder vielleicht eine andere Variable verwendet? Ich denke, die Konstantelinux
wird zum Testen von Betriebssystemen verwendet, z. B. wenn Sie eine plattformübergreifende Anwendung entwerfen und genau wissen müssen, welche API verwendet werden soll (Windows, Mac, Linux, BSD usw.). Es ist nicht in stdio.h, aber es ist immer noch definiert, ob der Kernel Linux ist. Der gleiche Code sollte keinen Fehler in Windows erzeugen, aber wahrscheinlich etwas wie Windows oder WINDOWS als Variable verwenden und umgekehrtAntworten:
In den alten Tagen (vor ANSI) war das Vordefinieren von Symbolen wie
unix
undvax
eine Möglichkeit, mit der Code zur Kompilierungszeit erkennen konnte, für welches System er kompiliert wurde. Damals gab es keinen offiziellen Sprachstandard (über das Referenzmaterial auf der Rückseite der ersten Ausgabe von K & R hinaus), und C-Code jeglicher Komplexität war normalerweise 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 gab, welche Bezeichner von der Implementierung verwendet werden könnten und welche für Programmierer reserviert waren, konnten Compiler-Autoren einfache Namen wie verwendenunix
und gingen davon aus, dass Programmierer diese Namen einfach nicht für ihre eigenen Zwecke verwenden würden.Mit dem ANSI C-Standard von 1989 wurden Regeln eingeführt, die einschränken, welche Symbole eine Implementierung rechtlich vordefinieren kann. Ein vom Compiler vordefiniertes Makro kann nur einen Namen haben, der mit zwei Unterstrichen beginnt, oder mit einem Unterstrich, gefolgt von einem Großbuchstaben. So können Programmierer Bezeichner verwenden, die nicht mit diesem Muster übereinstimmen und nicht in der Standardbibliothek verwendet werden.
Infolgedessen jeder Compiler, der vordefiniert
unix
oderlinux
nicht konform ist, da er keinen perfekt legalen Code kompilieren kann, der so etwas verwendetint linux = 5;
.Zufällig ist gcc standardmäßig nicht konform - es kann jedoch so eingestellt werden, dass es (ziemlich gut) mit den richtigen Befehlszeilenoptionen übereinstimmt:
Weitere Informationen 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__
es definiert ist (vorausgesetzt, Sie verwenden gcc oder einen damit kompatiblen Compiler). Weitere Informationen finden Sie im GNU C-Präprozessorhandbuch .Abgesehen davon weitgehend irrelevant: Der "Best One Liner" -Sieger des Internationalen Obfuscated C Code Contest von 1987 von David Korn (ja, der Autor der Korn Shell) nutzte das vordefinierte
unix
Makro:Es wird gedruckt
"unix"
, aber aus Gründen, die absolut nichts mit der Schreibweise des Makronamens zu tun haben.quelle
unix
und vordefiniertvax
" - Huh? Nach meinem Verständnis war in den alten Tagen die ganze Welt einvax
!unix
Definition zu tun hat ). Ich habe in einem Kommentar zu diesem Kern erklärt, warum, wenn Sie oder jemand anderes neugierig sind.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 die
-dM
Option, um alle Präprozessordefinitionen zu drucken. Da die Eingabe "Datei" leer ist, werden genau die vordefinierten Makros angezeigt. 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.Die "Gnu Standard" -Versionen auch
#define unix
. (Verwendenc11
undgnu11
erzeugt die gleichen Ergebnisse.)Ich nehme an, sie hatten ihre Gründe, aber es scheint mir, dass die Standardinstallation von gcc (mit der C-Code kompiliert wird,
-std=gnu89
sofern nicht anders angegeben) nicht konform und - wie in dieser Frage - überraschend ist. Das Verschmutzen des globalen Namespace mit Makros, deren Namen nicht mit einem Unterstrich beginnen, ist in einer konformen Implementierung nicht zulässig. (6.8.10p2: "Alle anderen vordefinierten Makronamen beginnen mit einem führenden Unterstrich, gefolgt von einem Großbuchstaben oder einem zweiten Unterstrich." Wie in Anhang J.5 (Portabilitätsprobleme) erwähnt, sind solche Namen jedoch häufig vordefiniert.)Als ich diese Antwort ursprünglich schrieb, konnte ich in gcc keine Dokumentation zu diesem Problem finden, aber ich habe sie schließlich entdeckt, nicht im von der C-Implementierung definierten Verhalten oder in C-Erweiterungen, sondern im
cpp
Handbuch Abschnitt 3.7.3 , wo es stellt fest, dass:quelle
-std=gnu89
ist die Standardeinstellung, und soweit ich weiß, ist dies die Standardeinstellung für Solaris, Linux und Mac OS X ( developer.apple.com/library/mac/documentation/Darwin/Reference/… für letztere), und das ist es derjenige, der den Namespace verschmutzt.c89
versucht, dem Standard zu entsprechen. Wenn dies der Standard wäre, hätte ich keine Beschwerde.Denn
linux
ist ein integriertes Makro definiert, wenn der Compiler auf Linux ausgeführt wird oder für (wenn es sich um einen Cross-Compiler handelt) Linux kompiliert wird.Es gibt viele solcher vordefinierten Makros. Mit GCC können Sie Folgendes verwenden:
um eine Liste der Makros zu erhalten. (Ich habe es nicht geschafft, GCC zur
/dev/null
direkten Annahme zu überreden , aber die leere Datei scheint in Ordnung zu funktionieren.) Mit GCC 4.8.1 unter Mac OS X 10.8.5 erhielt ich die folgende Ausgabe:Das sind 236 Makros aus einer leeren Datei. Als ich
#include <stdio.h>
die Datei hinzufügte , stieg die Anzahl der definierten Makros auf 505. Dazu gehören alle Arten von plattformidentifizierenden Makros.quelle
cpp -dM < /dev/null
linux
) ist auf meinem Mac OS X-Computer nicht definiert - es läuft nicht unter Linux (nun, es gibt eine VM unter Linux, aber ...). Es kann durch die Überschriften definiert werden, die durch enthalten sind<stdio.h>
; Das Ausführen einer leeren Datei ist möglicherweise nicht identisch./dev/null
als Quelldatei ohne verwenden,-x
da sie keine Erweiterung hat, sodass gcc nicht weiß, ob es sich um C oder C ++ handelt. Sie könnengcc -E -dM -x c /dev/null
oder verwendengcc -E -dm -x c++ /dev/null
, um die Liste abzurufen, ohne eine leere Datei erstellen zu müssen.cpp
(der Präprozessor) nichtgcc
. Beachten Sie auch , ich gebe es nicht/dev/null
als Eingabedatei, sondern ich bin mit Umleitung zu machen/dev/null
instdin
. Auch @KerrekSB: Auf meinem Debian-Rechnerlinux
steht auf der Liste.> emptyfile.c
oder: > emptyfile.c
oderdd if=/etc/passwd of=emptyfile.c count=0
oder ...Von
info gcc
(Hervorhebung von mir):(Im Beispiel wird vax anstelle von Linux verwendet, da es beim Schreiben möglicherweise beliebter war ;-).
Die Grundidee ist, dass GCC nur dann versucht, die ISO-Standards vollständig einzuhalten, wenn es mit der
-ansi
Option aufgerufen wird .quelle
-ansi
oderstd=cXX
, woXX
ist89
,90
,99
, oder11
, und entweder-pedantic
oder-pedantic-errors
. Auch das ist eine Vereinfachung; Einzelheiten finden Sie im Handbuch .Verwenden Sie diesen Befehl
um das zu bekommen
quelle