Technologieaustausch

[C-Sprache] - Detaillierte Erläuterung der Vorverarbeitung (Teil 1)

2024-07-12

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

1. Vordefinierte Symbole

Es gibt einige Einstellungen in der C-SpracheVordefinierte Symbole, können direkt verwendet werden, die vordefinierten Symbole sind在预处理期间处理的

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

  
Beispiel:

#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

  
Operationsergebnis:

Fügen Sie hier eine Bildbeschreibung ein

Ich möchte hier erwähnen, dass VS ANSI C (Standard C) nicht vollständig unterstützt; gcc gccGcc Unterstützt ANSI C (Standard C)
  

zwei,# definieren definierenDtFInt Konstanten (Symbole) definieren

Grundlegende Syntax:

# define name stuff
  • 1

Zum Beispiel:

#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
  • Der zweite Satz ist Faulheit, warum?
  • Der dritte Satz für fürFoder Kreisförmig初始化、判断、调整都可以省略, aber wenn das Urteil weggelassen wird, bedeutet dies, dass die Urteilsbedingung immer wahr istEndlosschleife
  • Das sollte man am besten nicht im vierten Spiel machen, da es leicht zu Ärger führen kann.
  • Der fünfte SatzZeilenfortsetzungszeichenEs besteht darin, Probleme nach der Verzweigung zu verhindern转义Zurück回车符 , sodass der Wagenrücklauf kein Wagenrücklauf mehr ist.Nach dem Zeilenfortsetzungszeichen darf nichts mehr stehen, drücken Sie“” Drücken Sie einfach die Eingabetaste, sonst erfolgt die Fortsetzung nicht mit der folgenden Codezeile.

  
Jetzt kommt das Problem: Verwenden Sie # definieren definierenDtFInt Sollten Sie beim Definieren einer Kennung diese am Ende hinzufügen?

Zum Beispiel:

#define MAX 1000
#define MAX 1000;

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

Fügen Sie hier eine Bildbeschreibung ein

Der obige Code plus, es scheint nur ein wenig überflüssig zu sein, hat aber keinen Einfluss auf die Ausführung des Programms.
Es scheint hinzuzufügen Oder nicht hinzufügen Es wird alles gut?
Ist es wirklich so?
  
Schauen wir uns das folgende Beispiel an:

//例一
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

Nach dem Austausch:

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

Drucken1000;Was bedeutet?

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

sonst sonsttmse Zu wem passen?
  
Sehen Sie, da stimmt etwas nichtverwenden # definieren definierenDtFInt Fügen Sie beim Definieren eines Indikators diesen nicht nach

  

drei,# definieren definierenDtFInt Makro definieren

  # definieren definierenDtFInt Der Mechanismus umfasst eine Bestimmung,允许把参数替换到文本中, wird diese Implementierung oft aufgerufenMakro (Marco) (Marco)marcooder Makro definieren (Makro definieren) (Makro definieren)DtFIntmacro
Der Unterschied zwischen Makros und den oben genannten Makrodefinitionsbezeichnern ist:Makro hat Parameter
  
So wird das Makro deklariert:

#define name(parament - list) stuff
  • 1

einer von ihnen Parament ParamentPARBintNT - Liste ListemichchST(Argumentliste) ist eine durch Kommas getrennte Liste von Symbolen, die in vorkommen können Zeug ZeugSTSieff Mitte
Notiz: Parament ParamentPARBintNT - Liste ListemichchST(Parameterliste)Linke KlammerMuss mit sein Name NameNamet nebenWenn zwischen ihnen Leerzeichen vorhanden sind, wird die Argumentliste als interpretiert Zeug ZeugSTSieff ein Teil von.
  

Beispiel:

//实现一个宏,计算一个数的平方
#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

  
Operationsergebnis:

Fügen Sie hier eine Bildbeschreibung ein

Wie Sie sehen, ist das Quadrat von 5 korrekt berechnet

Tatsächlich liegt jedoch ein Problem mit dem obigen Code vor. Bitte sehen Sie sich den folgenden Codeausschnitt an:

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

  
Operationsergebnis:

Fügen Sie hier eine Bildbeschreibung ein

Warum ist das so? Das Ergebnis von 5+1 ist 36 und 6 ∗ * 6 sollte 36 sein. Wie bist du auf 11 gekommen?

Wir wissen, dass das Problem bei Makros liegt宏是直接替换, dann ist das Ergebnis des direkten Ersetzens des obigen Codes:

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

5 + 6 + 1, das Ergebnis ist natürlich 11

Wir können dieses Problem leicht lösen, indem wir auf beiden Seiten der Makrodefinition Klammern hinzufügen.

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

Gibt es also kein Problem mit dieser Definition?Werfen wir einen Blick auf die folgende Makrodefinition

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

Wir haben in der Definition Klammern verwendet, um das vorherige Problem zu vermeiden, aber dieses Makro kann neue Fehler verursachen.

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

  
Operationsergebnis:

Fügen Sie hier eine Bildbeschreibung ein

Das Ausgabeergebnis ist nicht 100, sondern 55. Der Grund ist ähnlich wie oben. Es ist immer noch so Prioritätsfrage
  
Lösung:

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

Zusammenfassend bei der Verwendung von MakrosSparen Sie niemals an Klammernum unerwartete Interaktionen zwischen Operatoren in Parametern oder benachbarten Operatoren bei der Verwendung von Makros zu vermeiden.

  

4. Makroparameter mit Nebenwirkungen

Wenn ein Makroparameter mehr als einmal in der Definition eines Makros vorkommt, sofern der Parameter vorhanden istNebenwirkung, dann könnten Sie bei der Verwendung dieses Makros in Gefahr sein, was zu Folgendem führt:不可预测 das Ergebnis von. Nebenwirkungen sind dauerhafte Auswirkungen, wenn der Ausdruck ausgewertet wird.
  
Zum Beispiel:

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

Das folgende MAX-Makro demonstriert die Probleme, die durch Parameter mit Nebenwirkungen verursacht werden.

#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

  
Operationsergebnis:

Fügen Sie hier eine Bildbeschreibung ein

Warum ist das so?Lassen Sie es uns gemeinsam analysieren

z = ((X++) > (y++) ? (x++) : (y++))
  • 1
  • Machen Sie zunächst ein Urteil: xxX++ mit JJj++ Urteil, weil es Postfix ++ ist, wenn es beurteilt wird xxX ist 5, JJj ist 8, 8 &gt; 5
  • Nach der Beurteilung xxX ist 6, JJj für 9
  • Dann ausführen JJj ++, da ++ postfixiert ist, ist das Ergebnis 9
  • Dann fahre fort JJj Führen Sie eine automatische Inkrementierung durch. JJj Das Endergebnis ist 10

wir werden xxX Und JJj Bei der Übergabe an das Makro haben sich insbesondere die Ergebnisse geändert JJj, nach zwei Änderungen sagten Sie, es sei nicht schrecklich
Wenn ein Parameter mit Nebeneffekten an ein Makro übergeben wird und der Parameter mehr als einmal im Makro vorkommt, tritt auch der Nebeneffekt des Parameters mehr als einmal auf.
  

5. Regeln für den Makro-Ersatz

Erweitern Sie das Programm # definieren definierenDtFInt Die Definition von Symbolen und Makros umfasst mehrere Schritte

  • Beim Aufruf eines Makros zuerst参数进行检查, sehen Sie, ob es # enthält definieren definierenDtFInt Definiert标识符 .Wenn ja, werden sie zuerst ersetzt
  • Ersatztext folgt被插入an die ursprüngliche Stelle im Programm verschoben, wobei die Namen der Makroparameter durch ihre Werte ersetzt werden
#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)MAX(X,M) Mitte MMM wird zunächst durch 10 ersetzt und 10 in das Original eingefügt MMM Standort
  • Abschließend werden die Ergebnisse erneut gescannt, um festzustellen, ob sie etwas enthalten, das durch # verursacht wurde. definieren definierenDtFInt Wenn ja, wiederholen Sie den obigen Vorgang

MAX wird im obigen Code auch durch # dargestellt definieren definierenDtFInt DefiniertMakro .Bei der letzten Inspektion wurde sein Parameter M ersetzt. Diesmal ist es an der Zeit, ihn zu ersetzen.
   MAX ( x , 10 ) MAX(x, 10)MAX(X,10) wird ersetzt durch ( ( x ) &gt; ( 10 ) ((x) &gt; (10)((X)>(10) ? ? ? ( x ) : ( 10 ) ) (x):(10))(X):(10))
  
sicherlich,宏里面嵌套宏es ist auch in Ordnung

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

Ersetzen Sie zu diesem Zeitpunkt zuerst das Makro im Parameter und dann das gesamte Makro
Aber es sollte beachtet werden, dass dies不是递归, bei dem es sich lediglich um ein Makro handelt, das als Argument für ein anderes Makro dient.递归是宏内部又调用了宏本身
Auch wenn der Präprozessor nach # sucht definieren definierenDtFInt Beim Definieren eines SymbolsDer Inhalt von String-Konstanten wird nicht durchsucht
Was bedeutet das?Geben Sie einfach eine Kastanie und Sie werden es verstehen.

#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

Im obigen Codeprintf("M = %d", x);Mitte MMM wird nicht durch 10 ersetzt

  

6. Vergleich zwischen Makros und Funktionen

Das obige Makro wird verwendet, um den größeren Wert zweier Zahlen zu ermitteln. Wir können es vollständig als Funktion schreiben.

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

Wir haben festgestellt, dass sie alle die gleiche Funktion erfüllen.Aber für die Funktion „Finden der größeren von zwei Zahlen“ wird das Schreiben des Makros erforderlich sein更有优势Manche
  
Es gibt zwei Gründe:

  • benutzt für调用函数Und函数返回vonCodekann mehr Zeit in Anspruch nehmen, als diese kleine Berechnung tatsächlich durchzuführen (beim Aufrufen der Funktion).Stapelrahmen erstellen ).Daher ist die Makroverhältnisfunktion im Programm enthalten规模Und速度Besser denn je
  • Wichtiger函数的参数必须声明类型 , wodurch die Funktion nur für Ausdrücke des entsprechenden Typs verwendet wird.Im Gegenteil, dieses Makro kann auf viele Typen angewendet werden: Ganzzahl, lange Ganzzahl, Gleitkomma usw. können verwendet werden
    > Vergleichen.Die Parameter des Makros sindTypunabhängigvon

Werden wir in Zukunft immer Makros verwenden?Tatsächlich werden Makros nur für verwendetEinfache Berechnung, im Vergleich zu Makros nicht für komplexe und große Operationen und Funktionen geeignet Nachteile

  1. Bei jeder Verwendung eines Makros wird eine Kopie des Makrodefinitionscodes in das Programm eingefügt.Sofern das Makro nicht relativ kurz ist, kann dies der Fall sein大幅度增加程序的长度
  2. Makro ist没法调试von
  3. Makros sind typunabhängig, d. h.不够严谨
  4. Makros können zu Problemen mit der Betriebspriorität führen und dazu führen, dass das Programm nicht funktioniert容易出错

Aber manchmal können Makros Dinge tun, die Funktionen nicht können
  
Zum Beispiel:

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

Es gefällt uns nicht, es so zu schreiben malloc mallocmalloc Funktion ist zu umständlich, möchte ich大小Und类型Geben Sie es weiter, um Platz zu schaffen

Malloc(10, int);
  • 1

Können Funktionen dies tun?Nein, weilFunktionen können keine Typen übergeben
Und Makros können es, weil Makros es nicht könnenNicht überprüfenIhre ParameterTypvon

#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

  
Ein Vergleich von Makros und Funktionen:

Attribute# definieren definierenDtFIntMakro definierenFunktion
CodelängeBei jeder Verwendung wird Makrocode in das Programm eingefügt, mit Ausnahme sehr kleiner Makros, wodurch sich die Länge des Programms erheblich erhöht.Der Funktionscodewert erscheint an einer Stelle und jedes Mal, wenn die Funktion verwendet wird, wird derselbe Code an dieser Stelle aufgerufen.
AusführungsgeschwindigkeitSchnellerEs gibt einen zusätzlichen Overhead durch Funktionsaufrufe und -rückgaben (das Öffnen von Stack-Frames), also beginnen wir mit den langsameren.
Vorrang des OperatorsMakroparameter werden im Kontext aller umgebenden Ausdrücke ausgewertet, da die Priorität benachbarter Operatoren unvorhersehbare Folgen haben kann. Daher wird empfohlen, Makros mit mehr Klammern zu schreiben.Funktionsparameter werden beim Aufruf der Funktion nur einmal ausgewertet und ihr Ergebniswert wird an die Funktion übergeben, wodurch das Auswertungsergebnis des Ausdrucks vorhersehbarer wird.
Parameter mit NebenwirkungenParameter können an mehreren Stellen im Makro ersetzt werden. Wenn die Parameter des Makros mehrmals ausgewertet werden, kann die Parameterauswertung mit Nebenwirkungen zu unvorhersehbaren Ergebnissen führen.Funktionsparameter werden beim Übergeben von Parametern nur einmal aufgerufen und die Ergebnisse sind leichter vorherzusagen.
ParametertypDie Parameter des Makros haben nichts mit dem Typ zu tun. Solange die Operation an den Parametern zulässig ist, kann er für jeden Parametertyp verwendet werden.Die Parameter einer Funktion sind typabhängig. Sind die Parameter unterschiedlichen Typs, sind unterschiedliche Funktionen erforderlich, auch wenn sie die gleiche Aufgabe erfüllen.
debuggenDas Debuggen von Makros ist unpraktischFunktionen können Schritt für Schritt debuggt werden
RekursionMakros können nicht rekursiv seinFunktionen können rekursiv sein

Gibt es eine Möglichkeit, ihre Vorteile zu kombinieren?
Eingeführt in C++Inline-Funktion inline inlineInmInt ——Hat die Vorteile von Makros und Funktionen
Es wird so schnell wie ein Makro ausgeführt, hat aber die gleiche Wirkung wie eine Funktion