Dlaczego preprocesor C w GCC interpretuje słowo linux
(małe litery) jako stałą 1
?
test.c:
#include <stdio.h>
int main(void)
{
int linux = 5;
return 0;
}
Wynik $ gcc -E test.c
(zatrzymaj się po etapie przetwarzania wstępnego):
....
int main(void)
{
int 1 = 5;
return 0;
}
Co oczywiście powoduje błąd.
(BTW: Nie ma #define linux
w tym stdio.h
pliku).
c
linux
gcc
c-preprocessor
ahmedaly50
źródło
źródło
#undef linux
, a może używasz innej zmiennej? Myślę, że stałalinux
służy do testowania systemów operacyjnych, np. Jeśli projektujesz aplikację wieloplatformową i musisz dokładnie wiedzieć, którego interfejsu API użyć (Windows, Mac, Linux, BSD itp.). Nie ma go w stdio.h, ale nadal jest zdefiniowane, jeśli jądro to Linux. Ten sam kod nie powinien powodować błędów w systemie Windows, ale przy użyciu czegoś takiego jak windows lub WINDOWS, jak prawdopodobnie zmienna, i na odwrótOdpowiedzi:
W dawnych czasach (przed ANSI) predefiniowanie symboli, takich jak
unix
ivax
było sposobem, aby umożliwić kodowi wykrycie w czasie kompilacji, dla jakiego systemu był kompilowany. Nie było wówczas oficjalnego standardu językowego (poza materiałem referencyjnym na końcu pierwszej edycji K&R), a kod C o dowolnej złożoności był zwykle złożonym labiryntem#ifdef
s, aby uwzględnić różnice między systemami. Te definicje makr zostały na ogół ustawione przez sam kompilator, a nie zdefiniowane w pliku nagłówkowym biblioteki. Ponieważ nie istniały żadne rzeczywiste reguły określające, które identyfikatory mogłyby być używane przez implementację i które były zarezerwowane dla programistów, autorzy kompilatorów mogli swobodnie używać prostych nazw podobnychunix
i zakładali, że programiści po prostu unikaliby używania tych nazw do własnych celów.Norma ANSI C z 1989 r. Wprowadziła zasady ograniczające, które symbole implementacja mogłaby z góry zdefiniować. Makro predefiniowane przez kompilator może mieć nazwę zaczynającą się od dwóch znaków podkreślenia lub znaku podkreślenia, po którym następuje wielka litera, pozostawiając programistom swobodę używania identyfikatorów niepasujących do tego wzorca i nieużywanych w bibliotece standardowej.
W rezultacie każdy kompilator, który predefiniuje
unix
lublinux
jest niezgodny, ponieważ nie skompiluje idealnie legalnego kodu, który używa czegoś takiegoint linux = 5;
.Tak się składa, że gcc jest domyślnie niezgodny - ale można go dostosować (dość dobrze) za pomocą odpowiednich opcji wiersza poleceń:
Więcej informacji znajduje się w instrukcji gcc .
gcc będzie wycofywał te definicje w przyszłych wydaniach, więc nie powinieneś pisać kodu, który zależy od nich. Jeśli twój program musi wiedzieć, czy jest kompilowany dla systemu Linux, może sprawdzić, czy
__linux__
jest zdefiniowany (zakładając, że używasz gcc lub kompilatora, który jest z nim kompatybilny). Zobacz instrukcję preprocesora GNU C , aby uzyskać więcej informacji.Pomijając kwestię w dużej mierze nieistotną: zwycięzca Międzynarodowego Konkursu Obfuscated C Code w 1987 r. „Best One Liner” autorstwa Davida Korna (tak, autor Korn Shell) skorzystał ze wstępnie zdefiniowanego
unix
makra:Drukuje
"unix"
, ale z powodów, które absolutnie nie mają nic wspólnego z pisownią nazwy makra.źródło
unix
ivax
” - Hę? Rozumiałem, że w dawnych czasach cały świat byłvax
!unix
definicją). Wyjaśniłem, dlaczego w komentarzu na ten temat, jeśli ty lub ktokolwiek inny jesteś ciekawy.Wygląda to na (nieudokumentowane) „rozszerzenie GNU”: [ poprawka : w końcu znalazłem wzmiankę w dokumentacji. Zobacz poniżej.]
Poniższe polecenie wykorzystuje
-dM
opcję drukowania wszystkich definicji preprocesora; ponieważ wejściowy „plik” jest pusty, pokazuje dokładnie predefiniowane makra. Został uruchomiony z gcc-4.7.3 na standardowej instalacji Ubuntu. Widać, że preprocesor jest zgodny ze standardami. W sumie istnieje 243 makr z-std=gnu99
i 240 z-std=c99
; Przefiltrowałem dane wyjściowe pod kątem trafności.Wersje „standardowe GNU”
#define unix
. (Używaniec11
ignu11
daje te same wyniki).Przypuszczam, że mieli swoje powody, ale wydaje mi się, że domyślna instalacja gcc (która kompiluje kod C, o
-std=gnu89
ile nie podano inaczej) jest niezgodna i - jak w tym pytaniu - zaskakująca. Zanieczyszczenie globalnej przestrzeni nazw makrami, których nazwy nie zaczynają się znakiem podkreślenia, nie są dozwolone w zgodnej implementacji. (6.8.10p2: „Wszelkie inne wstępnie zdefiniowane nazwy makr powinny zaczynać się od wiodącego znaku podkreślenia, po którym następuje duża litera lub drugi znak podkreślenia”, ale, jak wspomniano w dodatku J.5 (problemy z przenośnością), takie nazwy są często predefiniowane.)Kiedy pierwotnie napisałem tę odpowiedź, nie byłem w stanie znaleźć żadnej dokumentacji w gcc na ten temat, ale w końcu ją odkryłem, nie w zachowaniu zdefiniowanym w implementacji C ani w rozszerzeniach C, ale w
cpp
podręczniku 3.7.3 , gdzie zauważa, że:źródło
-std=gnu89
jest to ustawienie domyślne i, o ile wiem, jest to ustawienie domyślne w systemach solaris, Linux i Mac OS X ( developer.apple.com/library/mac/documentation/Darwin/Reference/... dla tych ostatnich) i jest to ten, który zanieczyszcza przestrzeń nazw.c89
stara się dostosować do standardu, więc gdyby był to domyślny, nie miałbym zażalenia.Ponieważ
linux
jest to wbudowane makro zdefiniowane podczas działania kompilatora lub kompilacja dla (jeśli jest to kompilator krzyżowy) dla systemu Linux.Istnieje wiele takich predefiniowanych makr. Dzięki GCC możesz używać:
aby uzyskać listę makr. (Nie udało mi się przekonać GCC do
/dev/null
bezpośredniego zaakceptowania , ale wydaje się, że pusty plik działa OK.) Z GCC 4.8.1 działającym na Mac OS X 10.8.5, otrzymałem wynik:To 236 makr z pustego pliku. Kiedy dodałem
#include <stdio.h>
do pliku, liczba zdefiniowanych makr wzrosła do 505. Obejmuje to wszelkiego rodzaju makra identyfikujące platformę.źródło
cpp -dM < /dev/null
linux
) nie jest zdefiniowane na moim komputerze z systemem Mac OS X - nie działa pod Linuksem (cóż, jest maszyna wirtualna z systemem Linux, ale ...). Może być zdefiniowany przez nagłówki zawarte w<stdio.h>
; uruchomienie pustego pliku może nie być takie samo./dev/null
jako pliku źródłowego,-x
ponieważ nie ma rozszerzenia, więc gcc nie wie, czy jest to C czy C ++. Możesz użyćgcc -E -dM -x c /dev/null
lub,gcc -E -dm -x c++ /dev/null
aby uzyskać listę bez konieczności tworzenia pustego pliku.cpp
(preprocesor), że niegcc
. Należy również zauważyć, że nie dają go/dev/null
jako plik wejściowy, ale raczej używam przekierowanie do wprowadzania/dev/null
dostdin
. Również @KrekrekSB: na mojej maszynie debianlinux
znajduje się na liście.> emptyfile.c
lub: > emptyfile.c
lubdd if=/etc/passwd of=emptyfile.c count=0
…Od
info gcc
(moje podkreślenie):(W tym przykładzie używa vax zamiast linux, ponieważ kiedy został napisany, być może był bardziej popularny ;-).
Podstawową ideą jest to, że GCC stara się w pełni przestrzegać norm ISO tylko wtedy, gdy jest przywołana z
-ansi
opcją.źródło
-ansi
czystd=cXX
, gdzieXX
jest89
,90
,99
, lub11
, i albo-pedantic
albo-pedantic-errors
. Nawet to jest nadmierne uproszczenie; szczegóły w instrukcji obsługi .Użyj tego polecenia
żeby to zdobyć
źródło