Corso di C con Linux

Due direttive al preprocessore: #define e #undef

 

Come già sottolineato, una direttiva al preprocessore è un'istruzione diretta al compilatore, che espande l'ambiente di programmazione stesso.
Le direttive al preprocessore sono dette anche macro e il preprocessore viene indicato più precisamente col nome di preprocessore di macro.
In buona sostanza, il preprocessore C è un programma interno al compilatore, che esamina un file sorgente C e compie determinate modifiche su di esso, basandosi sulle direttive a lui destinate.
Per ora ci interessiamo solo a due macro, #define e #undef , mentre altre le tratteremo in seguito.

Molto spesso durante un programma si ha la necessità di sostituire un valore con un testo.
Ad es. supponiamo di dover creare un programma che utilizzi il valore matematico pi-greco: sarebbe preferibile utilizzare sempre la stringa PI_GRECO ad ogni occorrenza del valore, piuttosto che scrivere di volta in volta il valore 3.1415926536.

E’ in casi come questo che si rivela molto utile la macro #define sia per funzionalità che per leggibilità.
La forma generale è:

#define nome-macro testo-sostitutivo

oppure

#define nome-macro

Nel caso dell’esempio di pi-greco scriveremo:

#define PI_GRECO 3.1415926536

L’uso più frequente di #define in Linux è quello di condizionare la portabilità del codice, cioè se viene definita una data macro allora possono essere incluse o meno diverse estensioni della libreria standard C (glibc); in questo caso il nome-macro deve essere definito nella glibc.
L'esempio più classico di utilizzo è il seguente:

#define FALSO 0
#define VERO  1

Definendo questa direttiva, nel corpo del programma la macro FALSO viene sostituita con il valore 0 e la macro VERO con il valore 1.
Questa situazione è simile a quella vista nei Makefile: "Tutte le volte che una macro viene incontrata, la si sostituisce con il gesto ad essa corrispondente".
Date un’occhiata al seguente esempio che non necessita di ulteriori spiegazioni:

#include <stdio.h>
#include <stdlib.h>

#define FALSO 0
#define VERO 1

int main(void) {
   printf("VERO:%d\n", VERO);
   printf("FALSO:%d\nDue:%d\n”, FALSO, VERO+1);
   return(EXIT_SUCCESS);
   }


Un altro caso di #define che abbiamo già usato, inconsapevolemente, è EXIT_SUCCESS, dichiarato nella libreria standard del C.

Un utile uso di #define è definire i messaggi di errore che si possono ripetere durante l'esecuzione del programma, ad es.:

#define MSG_ERRORE "This is I LOVE YOU: try format to save your disk!"

Dopo una tale definizione, una possibile istruzione printf nel corpo del programma è:

printf(MSG_ERRORE);

Se siete dei buoni osservatori avrete certamente notato che tutte le macro definite sono maiuscole.
Questa è una convenzione, ormai consolidata tra i programmatori, che permette, durante la lettura di un programma, di rendersi conto a prima vista della presenza degli identificatori e quindi delle sostituzioni.

La direttiva #undef cancella la definizione associata alla macro da #define.

#define VALORE 1  /* imposto VALORE a 1 */
#undef VALORE     /* ma non mi piace, per esigenze preferisco 2 */
#define VALORE 2  /* imposto VALORE a 2 */


In questo caso se non fosse stata usata la #undef, avrei causato un messaggio di errore di "multiple define", ciò significa che le definizioni:

#define DISPARI 3
#define DISPARI 5


non sono lecite, in quanto la definizione di una macro deve essere unica.
Dopo che una definizione di macro è stata cancellata con #undef, ogni sua istanza non attiverà più la sostituzione corrispondente.
Questa possibilità del preprocessore viene solo usata in caso di compilazione condizionale, che vedremo più avanti durante il corso.

 

Torna all'indice Generale del corso di Corso di C con Linux di Software Planet