GNU/Linux >> Znalost Linux >  >> Linux

Proč preprocesor C interpretuje slovo linux jako konstantu 1?

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.


Linux
  1. Proč jsem přešel z Macu na Linux

  2. Můj příběh o Linuxu:Proč seznamovat lidi s Raspberry Pi

  3. Proč potřebuje uživatel root oprávnění sudo?

  1. Linux – Proč je kořenový adresář označen A / Sign?

  2. Linux – Proč detekce USB Stick trvá tak dlouho?

  3. Co znamená &na konci linuxového příkazu?

  1. Linux – Proč Setuid nefunguje?

  2. Proč se Ctrl + V nevloží do Bash (Linux shell)?

  3. Proč Linux zahřívá můj počítač?