Condivisione della tecnologia

[Linguaggio C] - Spiegazione dettagliata della preelaborazione (Parte 1)

2024-07-12

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

1. Simboli predefiniti

Ci sono alcune impostazioni in linguaggio CSimboli predefiniti, possono essere utilizzati direttamente, i simboli predefiniti sono在预处理期间处理的

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

  
Esempio:

#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

  
risultato dell'operazione:

Inserisci qui la descrizione dell'immagine

Vorrei menzionare qui che VS non supporta completamente ANSI C (standard C); gcc gccGc.c. Supporta ANSI C (standard C)
  

due,# definire definireDeFIne Definire costanti (simboli)

Sintassi di base:

# define name stuff
  • 1

Per esempio:

#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 seconda frase è pigrizia, perché?
  • La terza frase per perFO Circolare初始化、判断、调整都可以省略, ma se il giudizio viene omesso, significa che la condizione del giudizio è sempre vera, cioèciclo infinito
  • È meglio non farlo nel quarto gioco, potrebbe facilmente causare problemi.
  • La quinta frasecarattere di continuazione della rigaÈ prevenire problemi dopo la ramificazione. La sua essenza è转义Indietro回车符 , in modo che il ritorno a capo non sia più un ritorno a capo.Non può esserci nulla dopo il carattere di continuazione della riga, premere“” Basta premere Invio, altrimenti la continuazione non sarà la seguente riga di codice.

  
Ora arriva il problema: usa # definire definireDeFIne Quando definisci un identificatore, dovresti aggiungerlo alla fine?

Per esempio:

#define MAX 1000
#define MAX 1000;

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

Inserisci qui la descrizione dell'immagine

Il codice sopra più, sembra un po' ridondante, ma non ha alcun impatto sul funzionamento del programma.
Sembra aggiungere Oppure non aggiungere Andrà tutto bene?
è davvero così?
  
Diamo un'occhiata al seguente esempio:

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

Dopo la sostituzione:

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

Stampa1000;Cosa significa?

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

altrimenti altroelse Chi abbinare?
  
Vedi, c'è qualcosa che non va, quindiutilizzo # definire definireDeFIne Quando si definisce un indicatore, non aggiungerlo successivamente

  

tre,# definire definireDeFIne Definire la macro

  # definire definireDeFIne Il meccanismo prevede una disposizione允许把参数替换到文本中, questa implementazione viene spesso chiamataMacro (Marco) (Marco)marcoO Definire la macro (definiscimacro) (definisci macro)DeFInemacro
La differenza tra le macro e gli identificatori delle definizioni di macro sopra è:La macro ha parametri
  
Ecco come viene dichiarata la macro:

#define name(parament - list) stuff
  • 1

uno di loro paramento paramentoPUNRSonoeNT - elenco elencoliooST(elenco parametri) è un elenco separato da virgole di simboli che possono apparire in roba robaSTioeeff.. mezzo
Nota: paramento paramentoPUNRSonoeNT - elenco elencoliooST(lista parametri)Parentesi sinistraDeve essere con nome nomenomee accanto a, se esiste uno spazio bianco tra di loro, l'elenco degli argomenti viene interpretato come roba robaSTioeeff.. una parte di.
  

Esempio:

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

  
risultato dell'operazione:

Inserisci qui la descrizione dell'immagine

Come puoi vedere, il quadrato di 5 è calcolato correttamente

Ma in realtà c'è un problema con il codice sopra. Consulta il seguente frammento di codice:

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

  
risultato dell'operazione:

Inserisci qui la descrizione dell'immagine

Perché è così? Il risultato di 5+1 è 36 e 6 ∗ * 6 dovrebbe essere 36. Come hai ottenuto 11?

Il problema è con le macro, lo sappiamo宏是直接替换, il risultato della sostituzione diretta del codice precedente è:

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

5 + 6 + 1, il risultato è naturalmente 11

Possiamo facilmente risolvere questo problema aggiungendo parentesi su entrambi i lati della definizione della macro.

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

Quindi non c’è nessun problema con questa definizione?Diamo un’occhiata alla seguente definizione di macro

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

Abbiamo utilizzato le parentesi nella definizione per evitare il problema precedente, ma questa macro potrebbe causare nuovi errori.

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

  
risultato dell'operazione:

Inserisci qui la descrizione dell'immagine

Il risultato in uscita non è 100 ma 55. Il motivo è simile a quello sopra Domanda prioritaria
  
Soluzione:

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

In sintesi, quando si utilizzano le macroNon lesinare mai sulle parentesiper evitare interazioni impreviste tra operatori nei parametri o operatori adiacenti quando si utilizzano le macro.

  

4. Parametri macro con effetti collaterali

Quando un parametro macro appare più di una volta nella definizione della macro, se il parametro haeffetto collaterale, potresti essere in pericolo quando usi questa macro, con conseguente不可预测 il risultato di. Gli effetti collaterali sono effetti permanenti quando viene valutata l'espressione.
  
Per esempio:

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

La seguente macro MAX illustra i problemi causati dai parametri con effetti collaterali.

#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

  
risultato dell'operazione:

Inserisci qui la descrizione dell'immagine

Perché è così?Analizziamolo insieme

z = ((X++) > (y++) ? (x++) : (y++))
  • 1
  • Innanzitutto esprimi un giudizio: XXIX++ con aa ...e++ giudizio, perché è suffisso ++, quando si giudica XXIX sono 5, aa ...e è 8, 8 &gt; 5
  • Dopo aver giudicato XXIX sono le 6, aa ...e per 9
  • Quindi esegui aa ...e ++, poiché è postfisso ++, il risultato è 9
  • Quindi continua aa ...e Esegui l'incremento automatico, aa ...e Il risultato finale è 10

noi XXIX E aa ...e Quando sono passati alla macro, i risultati sono cambiati, soprattutto aa ...e, dopo due modifiche, hai detto che non è terribile
Quando un parametro con effetti collaterali viene passato a una macro e il parametro viene visualizzato più di una volta nella macro, anche l'effetto collaterale del parametro appare più di una volta.
  

5. Regole per la sostituzione delle macro

Espandi nel programma n. definire definireDeFIne Ci sono diversi passaggi coinvolti nella definizione di simboli e macro

  • Quando si chiama una macro, prima参数进行检查, controlla se contiene # definire definireDeFIne Definito标识符 .In tal caso, verranno sostituiti per primi
  • Segue il testo sostitutivo被插入nella posizione originale nel programma, con i nomi dei parametri macro sostituiti dai relativi valori
#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
  • Massimo ( x , M ) Massimo ( x , M )MUNX(X,M) mezzo MMM viene prima sostituito con 10 e 10 viene inserito nell'originale MMM posizione
  • Infine, i risultati vengono nuovamente scansionati per vedere se contengono qualcosa causato da # definire definireDeFIne simbolo definito, in tal caso, ripetere il processo sopra descritto

MAX nel codice sopra è rappresentato anche da # definire definireDeFIne DefinitoMacro .Nell'ultima ispezione, il suo parametro M è stato sostituito. Questa volta è giunto il momento di sostituirlo.
   MASSIMO ( x , 10 ) MASSIMO( x , 10 )MUNX(X,10) è sostituito da ( ( x ) &gt; ( 10 ) (( x ) &gt; (10)((X)>(10) ? ? ? ( x ) : ( 10 ) ) ( x ):(10))(X):(10))
  
certamente,宏里面嵌套宏va anche bene

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

A questo punto, sostituire prima la macro nel parametro, quindi sostituire l'intera macro
Ma va notato che questo不是递归, che è semplicemente una macro che funge da argomento per un'altra macro.递归是宏内部又调用了宏本身
Inoltre, quando il preprocessore cerca # definire definireDeFIne Quando si definisce un simbolo,Non viene ricercato il contenuto delle costanti stringa
Che cosa significa?Dai una castagna e capirai.

#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

Nel codice sopraprintf("M = %d", x);mezzo MMM non sarà sostituito da 10

  

6. Confronto tra macro e funzioni

La macro sopra viene utilizzata per trovare il valore più grande di due numeri. Possiamo scriverlo completamente come una funzione.

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

Abbiamo scoperto che svolgono tutti la stessa funzione.Ma per la funzione di "trovare il maggiore tra due numeri", la scrittura della macro lo farà更有优势Alcuni
  
Ci sono due ragioni:

  • usato per调用函数E函数返回Dicodicepotrebbe richiedere più tempo dell'esecuzione effettiva di questo piccolo calcolo (quando si chiama la funzioneCrea stack frame ).Pertanto, la funzione del rapporto macro è nel programma规模E速度Meglio che mai
  • Ma ancora più importante函数的参数必须声明类型 , che fa sì che la funzione venga utilizzata solo su espressioni del tipo appropriato.Al contrario, questa macro può essere applicata a molti tipi: è possibile utilizzare intero, intero lungo, virgola mobile, ecc.
    > Per confrontare.I parametri della macro sonoTipo indipendenteDi

Utilizzeremo sempre le macro in futuro?In effetti, le macro vengono utilizzate solo perCalcolo semplice, non adatto a quelle operazioni e funzioni complesse e di grandi dimensioni rispetto alle macro Svantaggi

  1. Ogni volta che viene utilizzata una macro, una copia del codice di definizione della macro viene inserita nel programma.A meno che la macro non sia relativamente breve, potrebbe大幅度增加程序的长度
  2. la macro è没法调试Di
  3. Le macro sono indipendenti dal tipo, ovvero不够严谨
  4. Le macro possono causare problemi di priorità operativa, causando problemi al programma容易出错

Ma a volte le macro possono fare cose che le funzioni non possono fare
  
Per esempio:

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

Non ci piace scriverlo così malloc mallocmaLLoc La funzione è troppo ingombrante, lo voglio大小E类型Passalo per aprire spazio

Malloc(10, int);
  • 1

Le funzioni possono farlo?No perchéLe funzioni non possono passare tipi
E le macro possono farlo, perché le macro noNon controllarei tuoi parametritipoDi

#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

  
Un confronto tra macro e funzioni:

Attributi# definire definireDeFIneDefinire la macrofunzione
lunghezza del codiceOgni volta che viene utilizzato, il codice macro verrà inserito nel programma Ad eccezione delle macro molto piccole, la lunghezza del programma aumenterà in modo significativo.Il valore del codice funzione appare in un posto e ogni volta che viene utilizzata la funzione, viene chiamato lo stesso codice in quel posto.
Velocità di esecuzionePiù veloceC'è il sovraccarico aggiuntivo delle chiamate di funzione e dei ritorni (apertura degli stack frame), quindi iniziamo con quelli più lenti.
precedenza dell'operatoreI parametri macro vengono valutati nel contesto di tutte le espressioni circostanti. A meno che non vengano incluse le parentesi, la precedenza degli operatori adiacenti potrebbe avere conseguenze imprevedibili. Pertanto, si consiglia di scrivere le macro con più parentesi.I parametri della funzione vengono valutati solo una volta quando viene chiamata la funzione e il relativo valore del risultato viene passato alla funzione, rendendo più prevedibile il risultato della valutazione dell'espressione.
Parametri con effetti collateraliI parametri possono essere sostituiti in più posizioni nella macro Se i parametri della macro vengono valutati più volte, la valutazione dei parametri con effetti collaterali può produrre risultati imprevedibili.I parametri della funzione vengono chiamati solo una volta durante il passaggio dei parametri e i risultati sono più facili da prevedere.
Tipo di parametroI parametri della macro non hanno nulla a che fare con il tipo. Finché l'operazione sui parametri è legale, può essere utilizzata per qualsiasi tipo di parametro.I parametri di una funzione sono correlati al tipo Se i parametri sono di tipo diverso, sono necessarie funzioni diverse, anche se eseguono la stessa attività.
eseguire il debugLe macro sono scomode per il debugÈ possibile eseguire il debug delle funzioni passo dopo passo
ricorsioneLe macro non possono essere ricorsiveLe funzioni possono essere ricorsive

Esiste un modo per combinare i loro vantaggi?
Introdotto in C++funzione in linea in linea in lineaInlIne ——Presenta i vantaggi sia delle macro che delle funzioni
Viene eseguito velocemente come una macro ma ha lo stesso effetto di una funzione