Fonctionnement du préprocesseur C

Comment fonctionne le code suivant, autrement dit, quel est l’algorithme du préprocesseur C? Est-ce que cela fonctionne sur tous les compilateurs?

#include  #define ba #define a 170 int main() { printf("%i", b); return 0; } 

Le préprocesseur remplace simplement b par a partout où il se trouve dans le programme, puis remplace a par 170 C’est simplement un remplacement textuel.

Fonctionne sur gcc.

C’est au § 6.10.3 (remplacement de macro):

6.10.3.4 Réparsing et remplacement ultérieur

1) Une fois que tous les parameters de la liste de remplacement ont été remplacés et que les traitements # et ## ont été effectués, tous les jetons de prétraitement du marqueur sont supprimés. Ensuite, la séquence de jetons de prétraitement résultante est réanalysée, ainsi que tous les jetons de prétraitement ultérieurs du fichier source, pour remplacer le nom de macros.

D’autres paragraphes énoncent des règles complémentaires et des exceptions, mais c’est fondamentalement cela.

Bien que cela puisse violer certaines définitions de “single pass”, c’est très utile. Comme le prétraitement récursif des fichiers inclus (§5.1.1.2p4).

Ce simple remplacement (d’abord b avec a puis a avec 170 ) devrait fonctionner avec n’importe quel compilateur. Vous devez être prudent avec les cas plus complexes (impliquant généralement la chaîne '#' caractères '#' et la concaténation de jetons '##' ) car il y a des cas de coin gérés différemment au moins par MSVC et gcc.

En cas de doute, vous pouvez toujours consulter la norme ISO ( un brouillon est disponible en ligne ) pour voir comment les choses sont censées fonctionner :). La section 6.10.3 est la plus pertinente dans votre cas.

Le préprocesseur remplace simplement les symboles séquentiellement à chaque fois qu’ils apparaissent. L’ordre des définitions n’a pas d’importance dans ce cas, b est remplacé par a premier et l’instruction printf devient

 printf("%i", a); 

et après a soit remplacé par 170 , il devient

 printf("%i", 170); 

Si l’ordre de définition a été modifié, c’est-à-dire

 #define a 170 #define ba 

Ensuite, le préprocesseur remplace a première, et la deuxième définition devient

 #define b 170 

Donc, finalement, la déclaration printf devient

 printf("%i",170); 

Cela fonctionne pour tout compilateur .

Pour obtenir des informations détaillées, essayez gcc -E afin d’parsingr la sortie de votre pré-processeur, ce qui peut facilement éliminer vos doutes.

#define atsortingbue simplement une valeur à un mot clé.

Ici, «b» se voit atsortingbuer la première valeur «a», puis «a» se voit atsortingbuer la valeur «170». Pour simplifier, cela peut être exprimé comme suit:

 b=a=170 

C’est juste une façon différente de définir la même chose.

Je pense que vous essayez d’obtenir des informations sur la manière dont le code source est traité par le compilateur. Pour savoir exactement, vous devez passer par les phases de traduction . Les étapes générales suivies par chaque compilateur (essayé de donner tous les détails – provenant de différents blogs et sites Web) sont les suivantes:

  1. Première étape par compilateur – Les caractères du fichier source physique sont mappés au jeu de caractères source (en introduisant des caractères de nouvelle ligne pour les indicateurs de fin de ligne) si nécessaire. Les séquences de sortinggraphes sont remplacées par des représentations internes à un caractère correspondantes.

  2. Deuxième étape par compilateur – Chaque instance d’un caractère de nouvelle ligne et d’un caractère de barre oblique inverse immédiatement précédent est supprimée, ce qui permet de fusionner les lignes source physiques pour former des lignes source logiques. Un fichier source qui n’est pas vide doit se terminer par un caractère de nouvelle ligne, qui ne doit pas être immédiatement précédé d’un caractère de barre oblique inversée.

  3. Troisième étape par compilateur – Le fichier source est décomposé en jetons de prétraitement et séquences de caractères d’espace blanc (y compris les commentaires). Un fichier source ne doit pas se terminer par un jeton ou un commentaire de prétraitement partiel. Chaque commentaire est remplacé par un espace. Les caractères de nouvelle ligne sont conservés. Que chaque séquence non vide d’autres caractères d’espace blanc soit conservée ou remplacée par un caractère d’espace est définie par l’implémentation.

  4. Quasortingème étape par compilateur – Les directives de prétraitement sont exécutées et les invocations de macros développées. Une directive de prétraitement #include entraîne le traitement récursif de l’en-tête nommé ou du fichier source de la phase 1 à la phase 4.

  5. Cinquième étape par complément – Chaque séquence d’échappement dans les constantes de caractère et les littéraux de chaîne est convertie en un membre du jeu de caractères d’exécution.

  6. Sixième étape par compilateur – Les jetons littéraux de chaîne de caractères adjacents sont concaténés et les jetons littéraux de chaîne large adjacents sont concaténés.

  7. Septième étape par compilateur – Les caractères d’espace blanc séparant les jetons ne sont plus significatifs. Les jetons de prétraitement sont convertis en jetons. Les jetons résultants sont analysés et traduits syntaxiquement et sémantiquement.

  8. Dernière étape – Toutes les références aux objects et fonctions externes sont résolues. Les composants de la bibliothèque sont liés pour satisfaire les références externes aux fonctions et aux objects non définis dans la traduction en cours. Tous ces résultats de traduction sont collectés dans une image de programme qui contient les informations nécessaires à l’exécution dans son environnement d’exécution.