[successivo] [precedente] [inizio] [fine] [indice generale] [indice ridotto] [indice analitico] [volume] [parte]


Capitolo 538.   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, tanto che anche le sue direttive sono state regolate con lo standard ANSI.

538.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.

538.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.

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.

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

538.1.2   Direttiva «#define»

#define macro [sequenza_di_caratteri]

La direttiva #define permette di definire dei nomi, conosciuti come macro, oppure «costanti simboliche» o «costanti manifeste». Quando queste macro vengono utilizzate nel sorgente, sono sostituite automaticamente con la sequenza che appare dopo la loro definizione.

#define SALUTO ciao come stai

L'esempio appena mostrato fa 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 viene utilizzato nella sostituzione.

#define SALUTO "ciao come stai"

Questo nuovo esempio è 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, mentre non sarebbe stato possibile quando la sostituzione è stata definita senza apici:

printf (SALUTO);

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, 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 è possibile vedere meglio in seguito, è sensato anche dichiarare una macro senza alcuna corrispondenza. Ciò può servire per le direttive #ifdef e #ifndef.

538.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.

538.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
...
...
int 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 1 000, il confronto è equivalente a 1 000 > 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
...
...
int main ()
{
#if NAZIONE==ita
    char valuta[] = "LIT";
    ...
#elsif NAZIONE==usa
    char valuta[] = "USD";
    ...
#endif
...
}

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

538.1.5   Direttive «#ifdef» e «#ifndef»

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

#define DEBUG
...
int 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
...
int 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.

538.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 */
...

538.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).

538.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"

538.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».

538.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 di informatica libera 2007.02 --- Copyright © 2000-2007 Daniele Giacomini -- <daniele (ad) swlibero·org>


Dovrebbe essere possibile fare riferimento a questa pagina anche con il nome c_istruzioni_del_preprocessore.htm

[successivo] [precedente] [inizio] [fine] [indice generale] [indice ridotto] [indice analitico]

Valid ISO-HTML!

CSS validator!