Zdá se, že se jedná o (nedokumentované) „rozšíření GNU“:[oprava :Konečně jsem našel zmínku v dokumentech. Viz níže.]
Následující příkaz používá -dM
možnost vytisknout všechny definice preprocesoru; protože je vstupní "soubor" prázdný, zobrazuje přesně předdefinovaná makra. Byl spuštěn s gcc-4.7.3 na standardní instalaci ubuntu. Můžete vidět, že preprocesor podporuje standardy. Celkem existuje 243 maker s -std=gnu99
a 240 s -std=c99
; Filtroval jsem výstup podle relevance.
$ 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
Verze "gnu standard" také #define unix
. (Pomocí c11
a gnu11
produkuje stejné výsledky.)
Předpokládám, že k tomu měli své důvody, ale zdá se mi, že je výchozí instalace gcc (která kompiluje kód C s -std=gnu89
pokud není uvedeno jinak) nevyhovující a – jako v této otázce – překvapivé. Znečištění globálního jmenného prostoru makry, jejichž názvy nezačínají podtržítkem, není v vyhovující implementaci povoleno. (6.8.10p2:"Jakékoli jiné předdefinované názvy maker musí začínat podtržítkem na začátku následovaným velkým písmenem nebo druhým podtržítkem," ale jak je uvedeno v příloze J.5 (problémy s přenositelností), takové názvy jsou často předdefinovány.)
Když jsem původně napsal tuto odpověď, nebyl jsem schopen najít žádnou dokumentaci v gcc o tomto problému, ale nakonec jsem to objevil, ne v chování definovaném implementací C ani v rozšíření C, ale v cpp
část manuálu 3.7.3, kde se uvádí, že:
Pomalu vyřazujeme všechna předdefinovaná makra, která jsou mimo vyhrazený jmenný prostor. Nikdy byste je neměli používat v nových programech…
Za starých časů (před ANSI) předdefinované symboly, jako je unix
a vax
byl způsob, jak umožnit kódu v době kompilace zjistit, pro jaký systém byl kompilován. Tehdy neexistoval žádný oficiální jazykový standard (kromě referenčního materiálu na zadní straně prvního vydání K&R) a kód C jakékoli složitosti byl obvykle složitým bludištěm #ifdef
s, aby se umožnily rozdíly mezi systémy. Tyto definice maker byly obecně nastaveny samotným kompilátorem, nejsou definovány v souboru záhlaví knihovny. Protože neexistovala žádná skutečná pravidla o tom, které identifikátory by mohla implementace používat a které byly vyhrazeny pro programátory, mohli autoři kompilátorů používat jednoduché názvy jako unix
a předpokládali, že programátoři se jednoduše vyhnou používání těchto jmen pro své vlastní účely.
Standard ANSI C z roku 1989 zavedl pravidla omezující, jaké symboly mohla implementace legálně předdefinovat. Makro předdefinované kompilátorem může mít pouze název začínající dvěma podtržítky nebo podtržítkem následovaným velkým písmenem, takže programátoři mohou volně používat identifikátory, které neodpovídají danému vzoru a nejsou použity ve standardní knihovně.
Výsledkem je jakýkoli kompilátor, který předdefinuje unix
nebo linux
je nevyhovující, protože selže sestavit dokonale legální kód, který používá něco jako int linux = 5;
.
Jak se stává, gcc je ve výchozím nastavení nevyhovující - ale lze jej upravit (přiměřeně dobře) pomocí správných možností příkazového řádku:
gcc -std=c90 -pedantic ... # or -std=c89 or -ansi
gcc -std=c99 -pedantic
gcc -std=c11 -pedantic
Další podrobnosti naleznete v příručce gcc.
gcc bude v budoucích verzích postupně vyřazovat tyto definice, takže byste neměli psát kód, který na nich závisí. Pokud váš program potřebuje vědět, zda je kompilován pro cíl Linux nebo ne, může zkontrolovat, zda __linux__
je definován (za předpokladu, že používáte gcc nebo kompilátor, který je s ním kompatibilní). Další informace naleznete v příručce k preprocesoru GNU C.
Velmi irelevantní stranou:vítěz „Best One Liner“ v roce 1987 International Obfuscated C Code Contest od Davida Korna (ano, autor Korn Shell) využil předdefinovaného unix
makro:
main() { printf(&unix["\021%six\012\0"],(unix)["have"]+"fun"-0x60);}
Vypíše "unix"
, ale z důvodů, které nemají absolutně nic společného s pravopisem názvu makra.