I macro del preprocessore in C sono stati introdotti come parte del linguaggio per garantire portabilità, leggibilità e facilità di configurazione del codice sorgente. Con le direttive #define, #ifdef, #ifndef è possibile creare sezioni di codice condizionali, dichiarare costanti e includere file esterni (#include).
Storia della questione:
Le direttive del preprocessore sono state introdotte per semplificare l'adattamento del codice sorgente a diversi sistemi e compilatori e automatizzare azioni ripetitive.
Problema:
Senza preprocessore non era possibile astrarsi dalle differenze di piattaforma, ripetere frammenti di codice, proteggersi dalla doppia inclusione di file di intestazione. Inoltre, bisogna prestare attenzione poiché i macro non sono tipizzati e non hanno ambito di visibilità.
Soluzione:
L'uso dei macro del preprocessore per dichiarare costanti, funzioni inline, compilazione condizionale e prevenire l'inclusione multipla di file di intestazione.
Esempio di codice:
#ifndef MY_HEADER_H #define MY_HEADER_H #define MAX_SIZE 100 #ifdef DEBUG #define LOG(x) printf("%s\n", x) #else #define LOG(x) #endif #endif /* MY_HEADER_H */
Caratteristiche chiave:
Cosa succede se dimentico la guardia di inclusione in un file di intestazione?
Il file di intestazione può essere incluso in un file .c più volte (implicitamente tramite altri .h), il che porterà a errori di ridefinizione (redefinition).
Qual è la differenza tra un macro #define e la dichiarazione di una funzione inline?
#define sostituisce semplicemente il testo, senza controllare tipi e correttezza sintattica, mentre una funzione inline è una normale funzione, controllata dal compilatore nella fase di analisi dei tipi.
I macro possono avere effetti collaterali?
Sì, se il macro è definito in modo impreciso, le espressioni passate possono essere calcolate più volte. Ad esempio:
#define SQUARE(x) (x) * (x) int a = SQUARE(++i); // ++i sarà eseguito DUE VOLTE!
Uso di un macro senza parentesi e con un'espressione che crea effetti collaterali:
#define DOUBLE(x) x + x int result = DOUBLE(1+2); // il risultato non è 6, ma 1+2+1+2=6? No, è 1+2+1+2=6 — giusto? No, diventa 1+2+1+2. // Ma in realtà sarà 1 + 2 + 1 + 2 = 6, ma se usi DOUBLE(++i), allora ++i sarà applicato due volte.
Pro:
Contro:
Definizione di un macro con parentesi e utilizzo per semplici costanti:
#define DOUBLE(x) ((x) + (x)) #define BUFFER_SIZE 1024
Pro:
Contro: