Partage de technologie

[Langage C] - Explication détaillée du prétraitement (Partie 1)

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

1. Symboles prédéfinis

Il y a quelques paramètres en langage CSymboles prédéfinis, peut être utilisé directement, les symboles prédéfinis sont在预处理期间处理的

__FILE__    //进行编译的源文件
__LINE__	//文件当前的行号
__DATE__	//文件被编译日期
__TIME__	//文件被编译那一瞬的时间
__STDC__	//如果编译器遵循ANSI C(标准C),其值为1,否则未定义(报错)
  • 1
  • 2
  • 3
  • 4
  • 5

  
Exemple:

#include<stdio.h>

int main()
{
	printf("%sn", __FILE__);
	printf("%dn", __LINE__);
	printf("%sn", __DATE__);
	printf("%sn", __TIME__);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

  
résultat de l'opération :

Insérer la description de l'image ici

Je voudrais mentionner ici que VS ne prend pas entièrement en charge ANSI C (standard C); gccgccgcc Prend en charge ANSI C (norme C)
  

deux,# définir définirdetttttttttttttttttttttttttttttttttFdansettttttttttttttttttttttttttttttttt Définir des constantes (symboles)

Syntaxe de base :

# define name stuff
  • 1

Par exemple:

#define MAX 100
#define reg register  // 为 register 这个关键字创建一个简短的名字
#define do_forever for(;;)  //用更形象的符号来替换一种实现
#define CASE break;case		//在写 case 语句的时候自动吧 break 写上

//如果定义的 stuff 过长,可以分成几行写,除了最后一行外,每行的后面都加一个反斜杠(续行符)
#define DEBUG_PRINT printf("file:%stline:%dt 
							date:%sttime:%sn" ,
							__FILE__,__LINE__ , 
							__DATE__,__TIME__ )
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • La deuxième phrase est la paresse, pourquoi ?
  • La troisième phrase pour pourFou Circulaire初始化、判断、调整都可以省略, mais si le jugement est omis, cela signifie que la condition du jugement est toujours vraie, c'est-à-direboucle infinie
  • Il est préférable de ne pas faire cela lors du quatrième match, cela peut facilement causer des problèmes.
  • La cinquième phrasecaractère de continuation de ligneIl s'agit d'éviter les problèmes après le branchement.转义Dos回车符 , afin que le retour chariot ne soit plus un retour chariot.Il ne peut y avoir rien après le caractère de continuation de ligne, appuyez sur“” Appuyez simplement sur Entrée, sinon la suite ne sera pas la ligne de code suivante.

  
Maintenant vient le problème : utilisez # définir définirdetttttttttttttttttttttttttttttttttFdansettttttttttttttttttttttttttttttttt Lors de la définition d’un identifiant, faut-il l’ajouter à la fin ?

Par exemple:

#define MAX 1000
#define MAX 1000;

int main()
{
	int n = MAX;
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

Insérer la description de l'image ici

Le code ci-dessus plus, cela semble juste un peu redondant, mais cela n'a aucun impact sur le fonctionnement du programme.
Il semble ajouter Ou n'ajoute pas Ça va aller?
est-ce vraiment comme ça ?
  
Regardons l'exemple suivant :

//例一
int main()
{
	printf("%dn", MAX);
	return 0;
}

//例二
int mian()
{
	int max = 0;
	if (1)
		max = MAX;
	else
		max = 1;
	return 0
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

Après remplacement :

printf("%dn", 1000;);
  • 1

Imprimer1000;Que signifie ?

if (1)
	max = 1000;
	;
else
	max = 1;
  • 1
  • 2
  • 3
  • 4
  • 5

autre autreetttttttttttttttttttttttttttttttttlse À qui correspondre ?
  
Vous voyez, il y a quelque chose qui ne va pas avec ça, alorsutiliser # définir définirdetttttttttttttttttttttttttttttttttFdansettttttttttttttttttttttttttttttttt Lors de la définition d'un indicateur, ne l'ajoutez pas après

  

trois,# définir définirdetttttttttttttttttttttttttttttttttFdansettttttttttttttttttttttttttttttttt Définir une macro

  # définir définirdetttttttttttttttttttttttttttttttttFdansettttttttttttttttttttttttttttttttt Le mécanisme comprend une disposition,允许把参数替换到文本中, cette implémentation est souvent appeléeMacro (marco) (marco)maisisisrcoou Définir une macro ( definemacro ) (define macro)detttttttttttttttttttttttttttttttttFdansetttttttttttttttttttttttttttttttttmaisisiscro
La différence entre les macros et les identifiants de définition de macro ci-dessus est :La macro a des paramètres
  
Voici comment la macro est déclarée :

#define name(parament - list) stuff
  • 1

l'un d'eux paragraphe paragraphepunlsuisetttttttttttttttttttttttttttttttttnt - liste listeljemt(liste de paramètres) est une liste de symboles séparés par des virgules qui peuvent apparaître dans des trucs des trucsmttoiff milieu
Note: paragraphe paragraphepunlsuisetttttttttttttttttttttttttttttttttnt - liste listeljemt(liste des paramètres)Parenthèse gaucheDoit être avec nom nomnomettttttttttttttttttttttttttttttttt près de, s'il existe un espace entre eux, la liste d'arguments est interprétée comme des trucs des trucsmttoiff une partie de.
  

Exemple:

//实现一个宏,计算一个数的平方
#define SQUARE(x) x*x

int main()
{
	int a = 5;
	int ret = SQUARE(a);
	printf("%dn", ret);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

  
résultat de l'opération :

Insérer la description de l'image ici

Comme vous pouvez le constater, le carré de 5 est correctement calculé

Mais en fait, il y a un problème avec le code ci-dessus. Veuillez consulter l'extrait de code suivant :

int a = 5;
printf("%dn", SQUARE(a + 1));
  • 1
  • 2

  
résultat de l'opération :

Insérer la description de l'image ici

Pourquoi cela est-il ainsi? Le résultat de 5+1 est 36, et 6 ∗ * 6 devrait être 36. Comment avez-vous obtenu 11 ?

Le problème vient des macros, nous le savons宏是直接替换, alors le résultat du remplacement direct du code ci-dessus est :

printf("%dn", a+1*a+1);
  • 1

5 + 6 + 1, le résultat est naturellement 11

Nous pouvons facilement résoudre ce problème en ajoutant des parenthèses des deux côtés de la définition de la macro.

#define SQUARE(x) (x)*(x)
  • 1

Il n'y a donc aucun problème avec cette définition ?Jetons un coup d'œil à la définition de macro suivante :

#define DOUBLE(X) (X)+(X)
  • 1

Nous avons utilisé des parenthèses dans la définition pour éviter le problème précédent, mais cette macro peut provoquer de nouvelles erreurs.

int a = 5;
printf("%dn", 10 * DOUBLE(a));
  • 1
  • 2

  
résultat de l'opération :

Insérer la description de l'image ici

Le résultat de sortie n’est pas 100 mais 55. La raison est similaire à celle ci-dessus. Question prioritaire
  
Solution:

#define DOUBLE(X) ((X)+(X))
  • 1

En résumé, lors de l'utilisation de macrosNe lésinez jamais sur les parenthèsespour éviter les interactions inattendues entre les opérateurs dans les paramètres ou les opérateurs adjacents lors de l'utilisation de macros.

  

4. Paramètres macro avec effets secondaires

Lorsqu'un paramètre de macro apparaît plus d'une fois dans la définition de la macro, si le paramètre aeffet secondaire, alors vous pourriez être en danger en utilisant cette macro, ce qui entraînerait不可预测 Le résultat de. Les effets secondaires sont des effets permanents lorsque l’expression est évaluée.
  
Par exemple:

x + 1; //不带副作用
x++//带副作用
  • 1
  • 2

La macro MAX suivante illustre les problèmes causés par les paramètres ayant des effets secondaires.

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

int main()
{
	int x = 5;
	int y = 8;
	int z = MAX(x++, y++);
	printf("x=%d, y=%d, z=%dn", x, y, z);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

  
résultat de l'opération :

Insérer la description de l'image ici

Pourquoi cela est-il ainsi?Analysons-le ensemble

z = ((X++) > (y++) ? (x++) : (y++))
  • 1
  • Portez d’abord un jugement : xxX++ avec ouaiset++ jugement, car c'est postfix ++, pour juger xxX est 5, ouaiset est 8, 8 &gt; 5
  • Après avoir jugé xxX est 6, ouaiset pour 9
  • Puis exécutez ouaiset ++, car il est postfixé ++, le résultat est 9
  • Puis continuez ouaiset Effectuer une incrémentation automatique, ouaiset Le résultat final est 10

nous allons xxX et ouaiset Une fois passés dans la macro, les résultats ont changé, notamment ouaiset, après deux changements, tu as dit que c'était pas terrible
Lorsqu'un paramètre avec des effets secondaires est transmis à une macro et que le paramètre apparaît plus d'une fois dans la macro, l'effet secondaire du paramètre est également plus d'une fois.
  

5. Règles de remplacement des macros

Développer dans le programme # définir définirdetttttttttttttttttttttttttttttttttFdansettttttttttttttttttttttttttttttttt La définition des symboles et des macros comporte plusieurs étapes.

  • Lorsque vous appelez une macro, commencez par参数进行检查, voyez s'il contient # définir définirdetttttttttttttttttttttttttttttttttFdansettttttttttttttttttttttttttttttttt Défini标识符 .Si c'est le cas, ils sont remplacés en premier
  • Le texte de remplacement suit被插入à l'emplacement d'origine dans le programme, avec les noms des paramètres de macro remplacés par leurs valeurs
#define MAX(a,b) ((a) > (b) ? (a):(b))
#define M 10

int main()
{
	int x = 5;
	int z = MAX(x, M);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • MAX ( x , M ) MAX(x, M)MUNX(X,M) milieu MMM est d'abord remplacé par 10, et 10 est inséré dans l'original MMM emplacement
  • Enfin, les résultats sont analysés à nouveau pour voir s'ils contiennent quelque chose causé par # définir définirdetttttttttttttttttttttttttttttttttFdansettttttttttttttttttttttttttttttttt symbole défini, si c'est le cas, répétez le processus ci-dessus

MAX dans le code ci-dessus est également représenté par # définir définirdetttttttttttttttttttttttttttttttttFdansettttttttttttttttttttttttttttttttt DéfiniMacro .Lors du dernier contrôle, son paramètre M a été remplacé. Il est temps de le remplacer cette fois.
   MAX ( x , 10 ) MAX(x, 10)MUNX(X,10) est remplacé par ( ( x ) &gt; ( 10 ) ((x) &gt; (10)((X)>(10) ? ? ? ( x ) : ( 10 ) ) (x):(10))(X):(10))
  
certainement,宏里面嵌套宏c'est bien aussi

MAX(x, MAX(2, 3))
  • 1

À ce stade, remplacez d'abord la macro dans le paramètre, puis remplacez la macro entière
Mais il faut noter que cela不是递归, qui n'est qu'une macro qui sert d'argument à une autre macro.递归是宏内部又调用了宏本身
De plus, lorsque le préprocesseur recherche # définir définirdetttttttttttttttttttttttttttttttttFdansettttttttttttttttttttttttttttttttt Lors de la définition d'un symbole,Le contenu des constantes chaîne n'est pas recherché
Qu'est-ce que cela signifie?Donnez juste une châtaigne et vous comprendrez.

#define M 10

int main()
{
	int x = 5;
	int z = MAX(x, MAX(2, 3));
	printf("M = %d", x);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

Dans le code ci-dessusprintf("M = %d", x);milieu MMM ne sera pas remplacé par 10

  

6. Comparaison entre macros et fonctions

La macro ci-dessus est utilisée pour trouver la plus grande valeur de deux nombres. Nous pouvons l’écrire complètement sous forme de fonction.

int Max(int x, int y)
{
	return x > y ? x : y;
}
  • 1
  • 2
  • 3
  • 4

Nous avons constaté qu’ils remplissent tous la même fonction.Mais pour la fonction "trouver le plus grand de deux nombres", l'écriture de la macro sera更有优势Quelques
  
Il y a deux raisons :

  • utilisé pour调用函数et函数返回decodepeut prendre plus de temps que l'exécution réelle de ce petit calcul (lors de l'appel de la fonctionCréer un cadre de pile ).Par conséquent, la fonction macro ratio est dans le programme规模et速度Mieux que jamais
  • Plus important函数的参数必须声明类型 , ce qui entraîne l'utilisation de la fonction uniquement sur les expressions du type approprié.Au contraire, cette macro peut s'appliquer à de nombreux types : entier, entier long, virgule flottante, etc.
    > Comparer.Les paramètres de la macro sontType indépendantde

Utiliserons-nous toujours des macros à l’avenir ?En fait, les macros ne sont utilisées que pourCalcul simple, ne convient pas aux opérations et fonctions complexes et volumineuses par rapport aux macros Désavantages

  1. Chaque fois qu'une macro est utilisée, une copie du code de définition de la macro est insérée dans le programme.À moins que la macro soit relativement courte, elle peut大幅度增加程序的长度
  2. la macro est没法调试de
  3. Les macros sont indépendantes du type, c'est-à-dire不够严谨
  4. Les macros peuvent provoquer des problèmes de priorité d'opération, provoquant l'arrêt du programme.容易出错

Mais parfois, les macros peuvent faire des choses que les fonctions ne peuvent pas faire.
  
Par exemple:

int* p = (int*)malloc(10 * sizeof * (int));
  • 1

Nous n'aimons pas l'écrire comme ça malloc mallocmaisisislloc La fonction est trop lourde, je veux大小et类型Passez-le pour ouvrir de l'espace

Malloc(10, int);
  • 1

Les fonctions peuvent-elles faire cela ?Non parce queLes fonctions ne peuvent pas transmettre de types
Et les macros peuvent le faire, parce que les macros ne le font pas.Ne vérifie pasvos paramètrestaperde

#define Malloc(n, type) (type*)malloc(n * sizeof(type))

int main()
{
	int* p = Malloc(10, int);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

  
Une comparaison des macros et des fonctions :

Les attributs# définir définirdetttttttttttttttttttttttttttttttttFdansetttttttttttttttttttttttttttttttttDéfinir une macrofonction
longueur du codeChaque fois qu'il est utilisé, du code macro sera inséré dans le programme. À l'exception des très petites macros, la longueur du programme augmentera considérablement.La valeur du code de fonction apparaît à un endroit et chaque fois que la fonction est utilisée, le même code à cet endroit est appelé.
Vitesse d'exécutionplus rapideIl y a la surcharge supplémentaire liée aux appels et aux retours de fonctions (ouverture des cadres de pile), alors commençons par les plus lents.
priorité des opérateursLes paramètres de macro sont évalués dans le contexte de toutes les expressions environnantes. À moins que des parenthèses ne soient incluses, la priorité des opérateurs adjacents peut avoir des conséquences imprévisibles. Par conséquent, il est recommandé d'écrire des macros avec plus de parenthèses.Les paramètres de fonction ne sont évalués qu'une seule fois lorsque la fonction est appelée, et sa valeur de résultat est transmise à la fonction, ce qui rend le résultat de l'évaluation de l'expression plus prévisible.
Paramètres avec effets secondairesLes paramètres peuvent être remplacés à plusieurs positions dans la macro. Si les paramètres de la macro sont évalués plusieurs fois, l'évaluation des paramètres avec des effets secondaires peut produire des résultats imprévisibles.Les paramètres de fonction ne sont appelés qu'une seule fois lors du passage des paramètres, et les résultats sont plus faciles à prédire.
Type de paramètreLes paramètres de la macro n'ont rien à voir avec le type. Tant que l'opération sur les paramètres est légale, elle peut être utilisée avec n'importe quel type de paramètre.Les paramètres d'une fonction sont liés au type. Si les paramètres sont de types différents, différentes fonctions sont requises, même si elles effectuent la même tâche.
déboguerLes macros ne sont pas pratiques à déboguerLes fonctions peuvent être déboguées étape par étape
récursivitéLes macros ne peuvent pas être récursivesLes fonctions peuvent être récursives

Existe-t-il un moyen de combiner leurs avantages ?
Introduit en C++fonction en ligne en ligne en lignedansldansettttttttttttttttttttttttttttttttt ——A les avantages des macros et des fonctions
Il s'exécute aussi vite qu'une macro mais a le même effet qu'une fonction