[inizio] [indice generale] [precedente] [successivo] [indice analitico] [contributi]


158. C: istruzioni del preprocessore

Il preprocessore è un programma, o quella parte del compilatore, che si occupa di pre-elaborare un sorgente prima della compilazione vera e propria. In pratica, permette di generare un nuovo sorgente prima che questo venga compilato effettivamente. L'utilità della presenza di un preprocessore, tra le altre cose, sta nella possibilità di definire gruppi di istruzioni alternativi a seconda di circostanze determinate.

Il linguaggio C non può fare a meno della presenza di un preprocessore, e anche le sue direttive sono state regolate con lo standard ANSI.

158.1 Linguaggio a sé stante

Le direttive del preprocessore rappresentano un linguaggio a sé stante, con le sue regole particolari. In generale:

Nelle sezioni seguenti vengono descritte le direttive più importanti.

158.1.1 Direttiva #include

#include <<file>>

#include "<file>"

La direttiva `#include' permette di includere un file. Generalmente si tratta di un cosiddetto file di intestazione, contenente una serie di definizioni necessarie al file sorgente in cui vengono incorporate.

Come si vede dalla sintassi, il file può essere indicato delimitandolo con le parentesi angolari, oppure con gli apici doppi.

#include <stdio.h>

#include "stdio.h"

Nel primo caso si fa riferimento a un file che dovrebbe trovarsi in una posizione stabilita dalla configurazione del compilatore (nel caso del C GNU in GNU/Linux, dovrebbe trattarsi della directory `/usr/include/'); nel secondo si fa riferimento a una posizione precisa, che richiede l'indicazione di un percorso se non si tratta della stessa posizione in cui si trova il sorgente in questione. *1*

Un file incorporato attraverso la direttiva `#include', può a sua volta includerne altri, e questa possibilità va considerata per evitare di includere più volte lo stesso file.

158.1.2 Direttiva #define

#define <macro> [<sequenza-di-caratteri>]

La direttiva `#define' permette di definire dei nomi, in questo caso si parla di macro (definite anche «costanti simboliche» o «costanti manifeste»), che, quando utilizzate nel sorgente, vengono sostituiti automaticamente con la sequenza che appare dopo la definizione. Per esempio,

#define SALUTO ciao come stai

farà in modo che il preprocessore sostituisca tutte le occorrenze di `SALUTO' con `ciao come stai'. È molto importante comprendere questo particolare: tutto ciò che appare dopo il nome della macro sarà utilizzato nella sostituzione. Per esempio,

#define SALUTO "ciao come stai"

è diverso dal caso precedente, perché ci sono in più gli apici doppi. Questa volta, la macro `SALUTO' potrebbe essere utilizzata in un'istruzione come quella seguente,

printf( SALUTO );

mentre non sarebbe stato possibile quando la sostituzione era stata definita senza apici.

Visto questo, si può osservare che questa direttiva può essere utilizzata in modo più complesso, facendo anche riferimento ad altre macro già definite.

#define UNO 1
#define DUE UNO+UNO
#define TRE DUE+UNO

In presenza di una situazione come questa, utilizzando la macro `TRE', si ottiene prima la sostituzione con `DUE+UNO', quindi con `UNO+UNO+1', e infine con `1+1+1' (dopo, tocca al compilatore).

L'utilizzo tipico delle macro è quello con cui si definiscono le dimensioni di qualcosa, per esempio gli array, e la corrispondenza reale di valori determinati che dipendono dalla piattaforma.

Per convenzione, i nomi utilizzati per le macro sono espressi solo con lettere maiuscole.

Come si vedrà meglio in seguito, è sensato anche dichiarare una macro senza alcuna corrispondenza. Ciò può servire per le direttive `#ifdef' e `#ifndef'.

158.1.3 Direttiva #define con argomento

#define <macro>(<argomento>) <sequenza-di-caratteri>

La direttiva `#define' può essere usata in modo simile a una funzione, per definire delle sostituzioni che includono in qualche modo un argomento. Seguendo l'esempio seguente, l'istruzione `i=DOPPIO(i)' si traduce in `i=(i)+(i)'.

#define DOPPIO(a)	(a)+(a)
...
...
i = DOPPIO(i);
...

Si osservi il fatto che, nella definizione, la stringa di sostituzione è stata composta utilizzando le parentesi. Questo permette di evitare problemi successivamente, nelle precedenze di valutazione delle espressioni.

158.1.4 Direttive #if, #else, #elif e #endif

#if <espressione>
	<espressione>
endif

Le direttive `#if', `#else', `#elif' e `#endif', permettono di delimitare una porzione di codice che debba essere utilizzato o ignorato in relazione a una certa espressione che può essere calcolata solo attraverso definizioni precedenti.

#define DIM_MAX 1000
...
...
main() {
...
#if DIM_MAX>100
    printf("Dimensione enorme.");
    ...
#else
    printf("Dimensione normale.");
    ...
#endif
...
}

L'esempio mostra in che modo si possa definire questa espressione, confrontando la macro `DIM_MAX' con il valore 100. Essendo stata dichiarata per tradursi in 1000, il confronto è equivalente a 1000>100 che risulta vero, pertanto il compilatore include solo le istruzioni relative.

In particolare, l'istruzione `#elif', come si può intuire, serve per costruire una catena di alternative else-if.

Gli operatori di confronto che si possono utilizzare per le espressioni logiche sono i soliti, in particolare, è bene ricordare che per valutare l'uguaglianza si usa l'operatore `=='.

#define NAZIONE ita
...
...
main() {
#if NAZIONE==ita
    char valuta[] = "LIT";
    ...
#elsif NAZIONE==usa
    char valuta[] = "USD";
    ...
#endif
...
}

Queste direttive condizionali possono essere annidate, e inoltre possono contenere anche altri tipi di direttiva del preprocessore.

158.1.5 Direttive #ifdef e #ifndef

Le direttive `#ifdef' e `#ifndef' si aggiungono a quelle descritte nella sezione precedente, e servono per definire l'inclusione o l'esclusione di codice in base all'esistenza o meno di una macro.

#define DEBUG
...
main() {
...
#ifdef DEBUG
    printf( "Punto di controllo n. 1\n");
    ...
#endif
...
}

L'esempio mostra il caso in cui sia dichiarata una macro `DEBUG' (che non si traduce in alcunché) e in base alla sua esistenza viene incluso il codice che mostra un messaggio particolare.

#define OK
...
main() {
#ifndef OK
    printf( "Punto di controllo n. 1\n");
    ...
#endif
...
}

L'esempio appena mostrato è analogo a quello precedente, con la differenza che la direttiva `#ifndef' permette la compilazione delle istruzioni che controlla solo se la macro indicata non è stata dichiarata.

L'uso delle direttive `#else' e `#endif' avviene nel modo già visto per la direttiva `#if'.

158.1.6 Direttiva #undef

#undef <macro>

La direttiva `#undef' permette di eliminare una macro a un certo punto del sorgente.

#define NAZIONE ita
...
/* In questa posizione, NAZIONE risulta definita */
...
#undef NAZIONE
...
/* In questa posizione, NAZIONE non è definita */
...

158.2 Macro predefinite

Il compilatore C ANSI prevede alcune macro predefinite. Il loro scopo è quello di annotare informazioni legate alla compilazione nel file eseguibile finale (evidentemente a fini diagnostici).

158.2.1 File sorgente

Il programma può accedere all'informazione sul nome del file sorgente e della riga originale. Questi dati sono contenuti, rispettivamente, nelle macro `__FILE__' e `__LINE__'. Questi dati possono essere alterati nel sorgente, utilizzando la direttiva `#line'.

#line <numero-riga> "<nome-file>"

158.2.2 Data di compilazione

La data e l'ora della compilazione sono accessibili attraverso le macro `__DATE__' e `__TIME__'. Il formato della prima macro è la consueta stringa «mese/giorno/anno» e quello della seconda è «ore:minuti:secondi».

158.2.3 C standard

Se il compilatore C che si utilizza è «standard», allora la macro `__STDC__' corrisponde al valore 1. Qualunque altro valore indica che non si tratta di un compilatore standard.

---------------------------

Appunti Linux 2000.04.12 --- Copyright © 1997-2000 Daniele Giacomini --  daniele @ pluto.linux.it


1.) Quando si indica un file da includere, delimitandolo con gli apici doppi e senza indicare alcun percorso, se non si trova il file nella directory corrente, il file viene cercato nella directory predefinita, come se fosse stato indicato tra le parentesi angolari.


[inizio] [indice generale] [precedente] [successivo] [indice analitico] [contributi]