GNU/Linux >> Tutoriels Linux >  >> Linux

Pourquoi le préprocesseur C interprète-t-il le mot linux comme la constante 1 ?

Cela semble être une "extension GNU" (non documentée) :[correction :J'ai enfin trouvé une mention dans la doc. Voir ci-dessous.]

La commande suivante utilise le -dM option pour imprimer toutes les définitions du préprocesseur ; puisque le "fichier" d'entrée est vide, il affiche exactement les macros prédéfinies. Il a été exécuté avec gcc-4.7.3 sur une installation Ubuntu standard. Vous pouvez voir que le préprocesseur est compatible avec les normes. Au total, il y a 243 macros avec -std=gnu99 et 240 avec -std=c99; J'ai filtré la sortie pour la pertinence.

$ 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

Les versions "gnu standard" aussi #define unix . (En utilisant c11 et gnu11 produit les mêmes résultats.)

Je suppose qu'ils avaient leurs raisons, mais il me semble faire l'installation par défaut de gcc (qui compile le code C avec -std=gnu89 sauf indication contraire) non conforme, et -- comme dans cette question -- surprenant. La pollution de l'espace de noms global avec des macros dont les noms ne commencent pas par un trait de soulignement n'est pas autorisée dans une implémentation conforme. (6.8.10p2 :"Tout autre nom de macro prédéfini doit commencer par un trait de soulignement suivi d'une lettre majuscule ou d'un second trait de soulignement", mais, comme mentionné dans l'annexe J.5 (problèmes de portabilité), ces noms sont souvent prédéfinis.)

Lorsque j'ai initialement écrit cette réponse, je n'ai trouvé aucune documentation dans gcc sur ce problème, mais je l'ai finalement découvert, pas dans le comportement défini par l'implémentation C ni dans les extensions C mais dans le cpp section 3.7.3 du manuel, où il est noté que :

Nous supprimons progressivement toutes les macros prédéfinies qui se trouvent en dehors de l'espace de noms réservé. Vous ne devriez jamais les utiliser dans de nouveaux programmes…


Dans l'ancien temps (pré-ANSI), prédéfinir des symboles tels que unix et vax était un moyen de permettre au code de détecter au moment de la compilation pour quel système il était compilé. Il n'y avait pas de norme de langue officielle à l'époque (au-delà du matériel de référence à la fin de la première édition de K&R), et le code C de toute complexité était généralement un labyrinthe complexe de #ifdef s pour tenir compte des différences entre les systèmes. Ces définitions de macros étaient généralement définies par le compilateur lui-même, et non définies dans un fichier d'en-tête de bibliothèque. Puisqu'il n'y avait pas de règles réelles sur les identifiants pouvant être utilisés par l'implémentation et ceux qui étaient réservés aux programmeurs, les auteurs du compilateur se sont sentis libres d'utiliser des noms simples comme unix et supposait que les programmeurs éviteraient simplement d'utiliser ces noms à leurs propres fins.

La norme ANSI C de 1989 a introduit des règles limitant les symboles qu'une implémentation pouvait légalement prédéfinir. Une macro prédéfinie par le compilateur ne pouvait avoir qu'un nom commençant par deux traits de soulignement, ou par un trait de soulignement suivi d'une lettre majuscule, laissant les programmeurs libres d'utiliser des identifiants ne correspondant pas à ce modèle et non utilisés dans la bibliothèque standard.

Par conséquent, tout compilateur qui prédéfinit unix ou linux n'est pas conforme, car il ne parviendra pas à compiler un code parfaitement légal qui utilise quelque chose comme int linux = 5; .

Il se trouve que gcc n'est pas conforme par défaut -- mais il peut être rendu conforme (assez bien) avec les bonnes options de ligne de commande :

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

Voir le manuel de gcc pour plus de détails.

gcc supprimera progressivement ces définitions dans les prochaines versions, vous ne devriez donc pas écrire de code qui en dépende. Si votre programme a besoin de savoir s'il est compilé pour une cible Linux ou non, il peut vérifier si __linux__ est défini (en supposant que vous utilisez gcc ou un compilateur compatible avec lui). Consultez le manuel du préprocesseur GNU C pour plus d'informations.

Un aparté largement hors de propos :le gagnant du "Best One Liner" du concours international de code C obfusqué de 1987, par David Korn (oui, l'auteur du Korn Shell) a profité du unix prédéfini macro :

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

Il imprime "unix" , mais pour des raisons qui n'ont absolument rien à voir avec l'orthographe du nom de la macro.


Linux
  1. Pourquoi j'ai fait le passage de Mac à Linux

  2. Mon histoire Linux :pourquoi présenter le Raspberry Pi aux gens

  3. Pourquoi l'utilisateur racine a-t-il besoin d'une autorisation Sudo ?

  4. Linux - Pourquoi le répertoire racine est-il désigné par un signe / ?

  5. Linux - Comment inspecter les informations de structure de répertoire d'un fichier Unix/linux ?

Pourquoi utiliser le bureau Pantheon pour Linux Elementary OS

Pourquoi un Lun World Wide ID commence-t-il par le numéro 3 dans Linux dm-multipath

Comment fonctionne la commande 'ls' sous Linux/Unix ?

Qu'est-ce que la table des processus Linux ? En quoi cela consiste?

Pourquoi Ctrl + V ne colle-t-il pas dans Bash (shell Linux) ?

Pourquoi Linux chauffe-t-il mon ordinateur ?