Il linguaggio C
Il preprocessore
La sostituzione di macro
Le compilazioni condizionali
L’inclusione di file
Fondamenti di Informatica I  a.a. 2008-09
1
Il preprocessore
Fondamenti di Informatica I  a.a. 2008-09
2
Introduzione  1
Il preprocessore del linguaggio C è un programma a sé stante,
che viene eseguito prima del compilatore, con propria sintassi e
grammatica, orientate alle linee
Le principali funzioni offerte dal preprocessore sono:
Elaborazione di macro
Inclusione di file sorgente
Compilazione condizionale, che consente di compilare porzioni
distinte di codice sorgente in dipendenza del valore di
un’espressione aritmetica
Tutte le direttive per il preprocessore iniziano con il carattere
diesis,  (il cancelletto), che deve essere il primo carattere della
linea a meno di spazi bianchi
Fondamenti di Informatica I  a.a. 2008-09
3
Introduzione  2
Le direttive del preprocessore possono apparire ovunque nel
codice sorgente (prima, dopo, o inframezzate a istruzioni C)
Una definizione di macro termina con un newline invece che con
un punto e virgola
Per suddividere una definizione di macro su più linee occorre
dunque inserire un backslash, \, immediatamente prima del
newline
Esempio:
define LONG_MACRO “Questa è una macro molto lunga\
che si estende su due linee”
Fondamenti di Informatica I  a.a. 2008-09
4
La sostituzione di macro  1
Per macro si intende un nome a cui è associata una stringa
testuale, detta corpo della macro
Per convenzione, i nomi di macro dovrebbero essere costituiti da
sole lettere maiuscole e dovrebbero essere significativi del
contenuto della macro
Per lo standard ANSI, due nomi di macro sono distinti se
differiscono in almeno uno dei primi 31 caratteri
Quando un nome di macro viene invocato nel codice al di fuori del
punto di definizione, viene sostituito dal corpo della macro: si ha
una espansione di macro
L’uso più comune delle macro consiste nella definizione di costanti
numeriche: l’uso diretto di costanti nel codice costituisce una
pratica di programmazione scadente, perché il programma diventa
difficile da (leggere e) manutenere
Fondamenti di Informatica I  a.a. 2008-09
5
La sostituzione di macro  2
Oltre alle macro che definiscono costanti, esiste un’ulteriore forma
di macro, simile ad una funzione C, che accetta argomenti che
possono essere utilizzati nel corpo della macro
La sintassi è:
,

define
)
Corpo di macro
Nome di macro
(
Argomento di
macro
Sintassi di una macro di tipo funzione
Esempio:
define MULT_BY_TWO(a) ((a)  (a))
Fondamenti di Informatica I  a.a. 2008-09
6
La sostituzione di macro  3
MULT_BY_TO può essere utilizzata all’interno del programma,
come una funzione: le prestazioni migliorano perché la somma
“costa” meno della moltiplicazione
j  MULT_BY_TWO(5);
j  10;
Il parametro attuale 5 viene sostituito al parametro formale a, in ogni
sua occorrenza all’interno del corpo della macro
Le parentesi che delimitano a ed il corpo della macro sono necessarie
per assicurare la corretta istanziazione del parametro quando la macro
viene espansa
I parametri di una macro non sono variabili: non è definito il loro
tipo, né viene loro assegnata memoria  non sono in conflitto con
variabili esistenti con lo stesso nome
Fondamenti di Informatica I  a.a. 2008-09
7
La sostituzione di macro  4
Le macro vengono normalmente eseguite più velocemente delle
funzioni, perché non è necessario il salvataggio degli argomenti
sullo stack
Esempio: Trasformazione da maiuscole a minuscole, nel caso di
codifica ASCII
define TO_LOWER(c) ((c)  (‘a’  ‘A’))
La conversione di funzioni in macro comporta effetti significativi
sui tempi di esecuzione del programma quando la frequenza di
attivazione della funzione è elevata
Fondamenti di Informatica I  a.a. 2008-09
8
Errori comuni  1
L’introduzione di un “;” al termine di una definizione di macro è un
errore molto diffuso e molto pericoloso
Esempio:
define size 10;
Il punto e virgola diviene parte integrante della stringa da
espandere, per cui
x  size;
x  10;;
L’errore non viene segnalato dal compilatore, che interpreta il
secondo punto e virgola come un’istruzione vuota
Viceversa, produce un errore l’espansione di…
int array[size];
Fondamenti di Informatica I  a.a. 2008-09
La linea a cui viene fatto riferimento
nel messaggio di errore è corretta!
9
Errori comuni  2
L’errore più pericoloso si verifica quando, a seguito dell’espansione
della macro, l’istruzione risultante è sintatticamente corretta, ma ha
una semantica non corrispondente alle attese
Esempio:
define GOOD_CONDITION (var  1);
………
while GOOD_CONDITION
foo();
while (var  1);
foo();
Il “;” che segue (var  1) viene interpretato come un’istruzione
vuota, che costituisce il corpo del ciclo while  la chiamata alla
funzione non fa parte del corpo del while e se var coincide con 1 si
produce un ciclo infinito
I compilatori prevedono un’opzione per eseguire solo il
preprocessore, così da esaminare il codice risultante dopo
l’espansione di tutte le macro
Fondamenti di Informatica I  a.a. 2008-09
10
Errori comuni  3
Altro errore molto diffuso è l’uso dell’operatore di assegnamento
nella definizione di una macro, in analogia all’inizializzazione di
variabili
L’errore può condurre ad anomalie nel codice di difficile
individuazione
Esempio:
define MAX  100
produrrebbe…
for (jMAX; j>0; j)
for (j100; j>0; j)
L’assegnamento diviene un’espressione relazionale sintatticamente
corretta, che il compilatore non evidenzia  errore difficile da
rilevare
Fondamenti di Informatica I  a.a. 2008-09
11
Errori comuni  4
La parentesi sinistra che racchiude il parametro (/i) deve seguire
immediatamente il nome della macro, senza la presenza di spazi
bianchi: l’errore viene “normalmente” segnalato in compilazione
Esempio:
define NEG_A_PLUS_F(a) ((a)  f)
produrrebbe…
j  NEG_A_PLUS_F(x);
j  (x)  f;
Se invece fosse stato inserito uno spazio bianco, l’espressione viene
espansa in
j  (a)(a)  f(x);
perfettamente lecita se a è un nome di variabile ed f una funzione
Fondamenti di Informatica I  a.a. 2008-09
12
L’uso del nome di macro
nella definizione
Esempio:
define sqrt(x) ((x<0) ? sqrt(x) : sqrt(x))
Lo standard ANSI stabilisce che, se un nome di macro compare
nella propria definizione, allora non viene espanso: si evita il
problema delle espansioni infinite
L’espansione della macro sqrt produrrebbe…
y  sqrt(5);
y  ((5<0) ? sqrt(5) : sqrt(5));
Nota: l’uso di un nome di macro, all’interno della propria
definizione, ha senso solo se esiste una funzione con lo stesso
nome
Fondamenti di Informatica I  a.a. 2008-09
13
Assenza di controllo per
gli argomenti di macro  1
Dal punto di vista operativo…
define MULT_BY_TWO(a) ((a)  (a))
int mult_by_to(a)
int a;
{
return aa;
}
…l’utilizzo di una macro o di una funzione non produce un
risultato equivalente; infatti:
Sul parametro della macro non viene eseguito alcun controllo di
tipo; la funzione (nell’esempio), invece, presuppone un
argomento intero e restituisce un valore intero
Se alla funzione viene passata una costante reale, il compilatore
può comportarsi diversamente, in dipendenza dell’esistenza del
prototipo (in assenza di prototipo il risultato è imprevedibile)
 MULT_BY_TO può ricevere un parametro a di tipo qualsiasi
Fondamenti di Informatica I  a.a. 2008-09
14
Assenza di controllo per
gli argomenti di macro  2
L’assenza di controlli di tipo sugli argomenti delle macro
aggiunge flessibilità alla programmazione
Esempio:
define MIN(a, b) ((a) < (b) ? (a) : (b))
funziona con a e b sia interi che reali
Fra funzioni e macro esiste anche una sostanziale differenza
sul controllo del numero dei parametri, tra definizione e
invocazione:
Nelle funzioni, il compilatore C effettua il controllo solo se
esiste un prototipo; in caso contrario la funzione viene
compilata
correttamente,
con
impredicibilità
del
comportamento in fase di esecuzione
Per le macro, si ha una segnalazione di errore in fase di
compilazione
Fondamenti di Informatica I  a.a. 2008-09
15
Gli effetti collaterali
negli argomenti di macro
Esempio:
a  MIN(b, c);
a  ((b)<c ? (b) : (c));
se b<c, b viene incrementato due volte
Per non incorrere in comportamenti indesiderati, occorre non
utilizzare, nelle chiamate di macro, operatori che implicano
effetti collaterali (operatori di incremento, decremento,
assegnamento, etc.)
Fondamenti di Informatica I  a.a. 2008-09
16
Il binding degli argomenti
L’uso di espressioni in cui le parentesi non siano utilizzate
correttamente, come argomenti di macro, può produrre
comportamenti indesiderati, a causa della precedenza degli
operatori e del binding
Esempio:
define SQUARE(a) a  a
Se si passa alla macro un’espressione aritmetica…
j  2  SQUARE(34);
j  2  3  4  3  4;
…che assegna il valore 22 a j, piuttosto che il valore corretto 98
 Il corpo e gli argomenti di una macro devono essere sempre
racchiusi fra parentesi
Fondamenti di Informatica I  a.a. 2008-09
17
La cancellazione di una definizione
di macro
La definizione di una macro mantiene la sua validità fino al termine
del file sorgente, o fino a quando non viene esplicitamente
“cancellata”, per mezzo della direttiva undef
Non è possibile ridefinire una macro senza prima averla cancellata
con l’uso della direttiva apposita, a meno che le definizioni
coincidano
Fondamenti di Informatica I  a.a. 2008-09
18
Macro vs Funzioni  1
Sia macro che funzioni consentono di rappresentare con un
singolo nome un insieme di operazioni
Vantaggi
Le macro sono più veloci, perché non richiedono le operazioni
connesse con le chiamate di funzione (salvataggio del contesto)
Il numero degli argomenti delle macro è sempre soggetto a
controllo da parte del compilatore
Non è imposto alcun vincolo sul tipo degli argomenti (la stessa
macro può essere utilizzata su più tipi di dati)
Fondamenti di Informatica I  a.a. 2008-09
19
Macro vs Funzioni  2
Svantaggi
Gli argomenti di macro vengono valutati ogni volta che
compaiono all’interno del corpo della macro  possibili effetti
collaterali indesiderati
Il corpo delle funzioni è compilato una sola volta: molteplici
chiamate alla stessa funzione usufruiscono dello stesso codice
eseguibile; le macro vengono espanse ad ogni occorrenza
Nelle macro non vi è controllo di tipo sugli argomenti; le
funzioni definite con prototipi controllano sia il numero che il
tipo degli argomenti
La fase di debugging per programmi contenenti macro è più
complicata: il codice sorgente è sottoposto a due fasi di
traduzione  il codice oggetto è più “distante” dal sorgente
Fondamenti di Informatica I  a.a. 2008-09
20
Le compilazioni condizionali  1
Il preprocessore consente di
selezionare le porzioni di
codice che devono essere
compilate,
attraverso
le
direttive if, else, elif,
endif
Esempio: if x  1
undef x
define x 0
elif x  2
undef x
define x 3
else
define y 4
endif
Fondamenti di Informatica I  a.a. 2008-09
Espressione
condizionale
if

Codice
sorgente C

elif

else
Codice
sorgente C

Espressione
condizionale
La sintassi delle direttive di
compilazione condizionale
endif
21
Le compilazioni condizionali  2
L’espressione condizionale contenuta in una direttiva if o elif
deve essere una costante, che non deve necessariamente essere
racchiusa tra parentesi (sono opzionali)
La direttiva elif è equivalente al costrutto else if del linguaggio C
I blocchi di istruzioni dipendenti da una direttiva condizionale di
preprocessore non sono racchiusi tra parentesi graffe, ma sono
delimitati da un’istruzione elif, else, o endif
Ogni blocco if può contenere un numero qualsiasi di blocchi elif,
ma un solo blocco else, che deve essere l’ultimo
Ogni blocco if deve essere terminato da una direttiva endif
Fondamenti di Informatica I  a.a. 2008-09
22
Le compilazioni condizionali  3
Inoltre…
Le macro che compaiono in un’espressione condizionale vengono
espanse prima della valutazione dell’espressione
Le direttive condizionali di preprocessore possono essere innestate
(come gli if nel linguaggio C)
Le istruzioni comprese in blocchi condizionali possono essere anche
istruzioni del linguaggio C
Le compilazioni condizionali sono particolarmente utili nella fase di
debugging, durante lo sviluppo di un programma, per attivare o
disattivare porzioni di codice
Le direttive if e endif controllano la compilazione delle istruzioni
C racchiuse nel blocco condizionale, non la loro esecuzione
Fondamenti di Informatica I  a.a. 2008-09
if DEBUG
if (exp_debug)
{
………
}
endif
23
Il controllo dell’esistenza
di una macro  1
Le direttive if e elif consentono la compilazione condizionale, in
dipendenza del valore di un’espressione aritmetica
Si può compilare in modo condizionale anche in dipendenza
dell’esistenza o meno di una macro, mediante le direttive ifdef,
ifndef e endif
ifdef TEST
printf(“Questo è un test. \n”);
else
printf(“Questo non è un test. \n”);
endif
Se la macro TEST è definita, viene compilata la prima printf(),
altrimenti viene compilata la seconda
Fondamenti di Informatica I  a.a. 2008-09
24
Il controllo dell’esistenza
di una macro  2
Nella maggior parte dei casi è possibile utilizzare if invece di ifdef
e ifndef, poiché un nome di macro viene espanso a zero se non è
definito
L’eccezione che richiede l’uso di ifdef e ifndef è costituita dalle
macro definite come zero
Esempio: Si supponga di definire la macro FALSE come zero…
FALSE viene comunque ridefinita
if !FALSE
se è stata definita come zero, mai
 define FALSE 0
altrimenti
endif
Soluzione
Fondamenti di Informatica I  a.a. 2008-09
ifndef FALSE

define FALSE 0
elif FALSE

undef FALSE

define FALSE 0
endif
25
L’inclusione di file  1
La direttiva include può presentarsi in uno dei due formati:
include <nome_file>
include “nome_file”
…nel primo caso, il preprocessore cerca il file in un insieme di
directory dipendenti dal sistema (ad esempio, in UNIX come in LINUX,
i file standard di include sono contenuti in /usr/include)
…nel secondo caso, il preprocessore cerca il file secondo le usuali
regole di ricerca per lo specifico sistema operativo (tipicamente la
ricerca viene effettuata nella directory corrente); se la ricerca fallisce,
si procede come nel primo caso
Il comando include consente di creare file di definizione comuni, i
file header, che possono essere condivisi da più file sorgente
Fondamenti di Informatica I  a.a. 2008-09
26
L’inclusione di file  2
I file header sono caratterizzati dall’estensione .h e contengono le
definizioni delle strutture dati, delle macro e dei dati globali,
necessari alla comunicazione fra moduli
Scopo dei file header è quello di sintetizzare in un unico file le
informazioni comuni, invece di replicarle in ogni file sorgente
 si semplifica la programmazione e la manutenzione del codice
Molti sistemi operativi (UNIX, LINUX) forniscono file header
contenenti la definizione di strutture interne del SO
Anche le librerie di runtime prevedono un insieme di file header
che occorre includere per poter richiamare le relative funzioni
Fondamenti di Informatica I  a.a. 2008-09
27
Scarica

Linguaggio C: il preprocessore.