Compiler + lien + instructions de prétraitement

Compiler + lien

Processus d'exécution du langage C

Nous savons que l'implémentation finale de la machine est le code binaire de la séquence 01, et ce que nous écrivons sous IDE tel que VS est en fait une chaîne de texte. Tout d'abord, le compilateur convertira tous les codes que nous écrivons en caractères (en utilisant des codes ASCⅡ) pour le stockage, puis les compilera en langage assembleur selon différentes instructions. À ce stade, le langage de haut niveau a atteint le niveau du langage machine, et le langage d'assemblage est à nouveau converti en binaire. Langue, générer un fichier obj (xxx.o), l'envoyer à l'éditeur de liens, générer un fichier exécutable (xxx.exe)
Insérez la description de l'image ici

Compiler

La compilation est divisée en trois étapes: la pré-compilation, la compilation et l'assemblage. Afin de mieux comprendre, nous pouvons comprendre le mode généré via la ligne de commande linux. Le fichier original est le suivant
Insérez la description de l'image ici

Précompilé

1. Le fichier d'en-tête contient
2. Le remplacement de #define
3. La suppression des commentaires
Insérez la description de l'image ici
GénérerInsérez la description de l'image ici

Compiler

A ce stade
1, analyse grammaticale
2, analyse lexicale
3, analyse sémantique
4,
Insérez la description de l'image ici
génération de résumé de symbolesInsérez la description de l'image ici

compilation

Cette étape du code d'assemblage en une instruction binaire, formant la table de symboles
# Insérez la description de l'image ici
généréeInsérez la description de l'image ici

lien

À ce stade
1, fusionnez la table de segments
2, fusionnez et déplacez la table de symboles pour
Insérez la description de l'image ici
générer
un fichier exécutable Le suffixe du fichier exécutable dans l'environnement linux est .out
Insérez la description de l'image ici

Directive de prétraitement

Symbole de prétraitement

FILE // Le fichier source à compiler
LINE // Le numéro de ligne actuel du fichier
DATE // La date à laquelle le fichier a été compilé
TIME // L'heure à
laquelle le fichier a été compilé STDC // Si le compilateur est conforme à ANSI C, son la valeur est 1, sinon indéfinie

Définition de macro

L'essence de la macro est de remplacer toutes les positions de variables définies qui apparaissent dans le thème du programme au stade de la recompilation, il est donc préférable de ne pas ajouter;

  1. Lors de l'appel de la macro, vérifiez d'abord les paramètres pour voir si elle contient des symboles définis par #define. S'ils le sont, ils sont remplacés en premier.
  2. Le texte de remplacement est ensuite inséré à la position du texte d'origine dans le programme. Pour les macros, les noms des paramètres sont remplacés par leurs valeurs.
  3. Enfin, scannez à nouveau le fichier de résultats pour voir s'il contient des symboles définis par #define. Si tel est le cas, répétez le processus ci-dessus.
#define M 100
// 如果定义的 stuff过长,可以分成几行写,除了最后一行外,每行的后面都加一个反斜杠(续行符)。
#define DEBUG_PRINT printf("file:%s\tline:%d\t \
                          date:%s\ttime:%s\n" ,\
                          __FILE__,__LINE__ ,       \
                          __DATE__,__TIME__ )  

Fonctions et fonctions de définition de macro

1. La macro peut simuler la définition de fonction

#define SQUARE(x) x*x
int main(){
    
    
	printf("%d",SQUARE(5));//替换后为 5*5=25
	return 0;
}

Cependant, cette méthode de définition peut provoquer des erreurs de priorité

#define SQUARE(x) x*x
int main(){
    
    
	printf("%d",SQUARE(5+1));//替换后为 5+1*5+1=11
	return 0;
}

Ou

#define double(x) (x)+(x)
int main(){
    
    
	printf("%d",2*SQUARE(5+1));//替换后为 2*(5+1)+(5+1)=18
	return 0;
}

Donc généralement

#define SQUARE(X) ((X)*(X))

2. Comparaison des macros et des fonctions
Les macros sont généralement utilisées pour effectuer des opérations simples

#define MAX(a, b) ((a)>(b)?(a):(b))

Alors pourquoi ne pas utiliser des fonctions pour accomplir cette tâche? Il y a deux raisons:

  1. Le code utilisé pour appeler la fonction et revenir de la fonction peut prendre plus de temps que l'exécution réelle de ce petit travail de calcul. Les macros sont donc meilleures que les fonctions en termes de
    taille et de vitesse du programme .
  2. Plus important encore, les paramètres de la fonction doivent être déclarés comme un type spécifique. Les fonctions ne peuvent donc être utilisées que sur des expressions du type approprié. Au contraire, comment cette macro peut-elle être
    appliquée aux types qui peuvent être comparés à>, tels que les entiers, les entiers longs et les types à virgule flottante. Les macros sont indépendantes du type.
    Bien entendu, par rapport aux macros, les fonctions présentent également des inconvénients:
  3. Chaque fois qu'une macro est utilisée, une copie du code de définition de macro sera insérée dans le programme. À moins que la macro ne soit relativement courte, elle peut augmenter considérablement la durée du programme.
  4. Les macros ne peuvent pas être déboguées.
  5. Étant donné que les macros ne sont pas indépendantes du type, elles ne sont pas assez rigoureuses.
  6. Les macros peuvent entraîner des problèmes de priorité des opérateurs, ce qui rend le processus sujet à des erreurs.
    Insérez la description de l'image ici

Quelques directives spéciales de prétraitement

1. # Remplacez la variable à remplacer sous forme de chaîne.
Par exemple: vous devez définir la macro print (format, valeur) pour que num soit imprimé au format

#define PRINT(FORMAT, VALUE)\ 
 printf("the value of " #VALUE "is "FORMAT "\n", VALUE);//打印出来为 the value of a is 10

2. ## Lier deux variables

#define ADD_TO_SUM(num, value) 
 sum##num += value; 
ADD_TO_SUM(5, 10);//作用是:给sum5增加10.

3. #undef est utilisé pour supprimer une définition et a l'effet inverse de #define
4. La définition de ligne de commande
contient le code suivant

#include <stdio.h> 
int main() 
{
    
     
 int array [ARRAY_SIZE]; 
 int i = 0; 
 for(i = 0; i< ARRAY_SIZE; i ++) 
 {
    
     
 array[i] = i; 
 } 
 for(i = 0; i< ARRAY_SIZE; i ++) 
 {
    
     
 printf("%d " ,array[i]); 
 } 
 printf("\n" ); 
 return 0; 
}

On observe que ARRAY_SIZE n'est pas défini,
et il peut être défini à l'aide de la ligne de commande dans l'environnement Linux

gcc -D ARRAY_SIZE=10 programe.c

4. Compilation conditionnelle
#s'il y a endif
#ifndef Si elle n'est pas définie, elle participera à la compilation, si elle est définie, elle ne participera pas à la compilation.
#Ifdef ou #if defined () Si elle est définie, elle le fera participer à la compilation. S'il n'est pas défini, il ne participera pas à la compilation.

1. 
#if 常量表达式
 //... 
#endif 
//常量表达式由预处理器求值。
如:
#define __DEBUG__ 1 
#if __DEBUG__ 
 //.. 
#endif 
2.多个分支的条件编译
#if 常量表达式
 //... 
#elif 常量表达式
 //... 
#else 
 //... 
#endif 
3.判断是否被定义
#if defined(symbol) 
#ifdef symbol 
#if !defined(symbol) 
#ifndef symbol 
4.嵌套指令
#if defined(OS_UNIX) 
 #ifdef OPTION1 
 unix_version_option1(); 
 #endif 
 #ifdef OPTION2 
 unix_version_option2(); 
 #endif 
#elif defined(OS_MSDOS) 
 #ifdef OPTION2 
 msdos_version_option2(); 
 #endif 
#endif

Insérez la description de l'image ici
Insérez la description de l'image ici
5. Empêchez les références multiples de fichiers d'en-tête. La
première méthode
Insérez la description de l'image ici
La deuxième méthode

#pragma once

Je suppose que tu aimes

Origine blog.csdn.net/weixin_54225634/article/details/115024062
conseillé
Classement