Il preprocessore C Funzionalità Inclusione di file Inclusione di file

Transcript

Il preprocessore C Funzionalità Inclusione di file Inclusione di file
2
Funzionalità

Il preprocessore C

Modifica il codice C prima che venga eseguita
la traduzione vera a propria
Le direttive al preprocessore riguardano:



Ver. 2.4



inclusione di file (#include)
definizione di simboli (#define)
sostituzione di simboli (#define)
compilazione condizionale (#if)
macroistruzioni con parametri (#define)
Non essendo istruzioni C non richiedono
il ‘;’ finale
© 2010 - Claudio Fornaro - Corso di programmazione in C
3
Inclusione di file



La riga con #include viene sostituita dal
contenuto testuale del file indicato
#include <stdio.h>
Il nome del file può essere completo di
percorso
Ha due forme:


#include <file.h>
Il file viene cercato nelle directory del compilatore
#include "file.h"
Il file viene cercato prima nella directory dove si
trova il file C e poi, se non trovato, nelle directory
del compilatore
4
Inclusione di file



I file inclusi possono a loro volta contenere
altre direttive #include
La direttiva #include viene in genere
collocata in testa al file sorgente C, prima
della prima funzione
Generalmente, i file inclusi non contengono
codice eseguibile, ma solo dichiarazioni (es.
prototipi e variabili extern) e altre direttive
5
Definizione di simboli





6
Sostituzione di simboli
#define nome
definisce il simbolo denominato nome
#define DEBUG
I simboli vengono utilizzati dalle altre direttive
del preprocessore (ad es. si può verificare se
un simbolo è stato definito o no con #if)
Lo scope di nome si estende dalla riga con la
definizione fino alla fine di quel file sorgente e
non tiene conto dei blocchi di codice
nome ha la stessa sintassi di un identificatore
(nome di variabile), non può contenere spazi e
viene per convenzione scritto in maiuscolo
#undef nome
annulla una #define precedente


Sono dette anche macro o macroistruzioni
#define nome testo_sostitutivo
sostituisce i caratteri di nome con i caratteri di
testo_sostitutivo in ogni punto del programma
dove nome appare come identificatore (ossia
sembra una variabile)
#define MAX 100
...

for (i=0; i<MAX; i++)
La sostituzione (detta anche “espansione”)
avviene prima della compilazione vera e
propria, quindi il compilatore non troverà la
riga di codice precedente, ma la seguente:
for (i=0; i<100; i++)
7
Sostituzione di simboli



nome non può essere definito più volte
nome ha la sintassi degli identificatori, viene
per convenzione scritto tutto maiuscolo se
testo_sostitutivo è una quantità costante (ossia
non si tratta di una macro con argomenti )
testo_sostitutivo termina a fine riga:




può contenere spazi
può utilizzare nomi di #define precedenti
se manca, si ha la semplice definizione di nome e
quindi tutte le occorrenze di nome sono eliminate
può continuare su più righe purché ogni riga da
continuare termini con il carattere ‘\’ (dal punto di
vista logico si tratta di una sola lunga riga)
8
Sostituzione di simboli



L’ordine di elencazione delle #define non è
rilevante: dopo una sostituzione su una riga,
questa viene considerata nuovamente per
verificare se quanto ottenuto può essere
sottoposto ad un’altra sostituzione
#define C 3
#define B C
#define A B
trasforma ogni A in B, ogni B in C e ogni C in
3, quindi tutte le A, le B e le C in 3
Se un nome è stato sostituito da un’espansione
e compare nuovamente per effetto di altre
espansioni, non viene sostituito nuovamente
9
Sostituzione di simboli


10
Macro con argomenti
Attenzione: essendo una semplice sostituzione
di caratteri, si può incorrere in errori
Esempio: il seguente ciclo non va fino a 202
#define MAX 100+1
...
for (i=0; i<MAX*2; i++)
In realtà va fino a 102 in quanto la sostituzione
produce il seguente codice:
for (i=0; i<100+1*2; i++)
Per evitare il problema a priori, è possibile
indicare il valore tra parentesi nella define:
#define MAX (100+1)




Sembra una chiamata di funzione con
parametri, invece è un’espansione di macro:
esecuzione più veloce, programma più grande
#define max(A,B) ((A)>(B)?(A):(B))
I simboli A e B sono sostituiti dagli argomenti
presenti nella “chiamata” della macro
La prima parte della #define non può avere
spazi intermedi, nell’utilizzo possono esserci
Esempio
x = max(p+q, r+s); viene espansa in
x = ((p+q)>(r+s)?(p+q):(r+s));
Si noti che funziona per tutti i tipi di dati
11
Macro con argomenti

Attenzione agli effetti collaterali:


12
Macro con argomenti

max(i++,j++) viene sostituita da
((i++)>(j++)?(i++):(j++))
e il valore più grande tra i e j viene incrementato
due volte
Attenzione alle parentesi:


#define quadrato(x) (x)*(x)
chiamata come quadrato(z+1) viene
correttamente sostituita da (z+1)*(z+1)
#define quadrato(x) x*x
chiamata come quadrato(z+1) viene
erroneamente sostituita da z+1*z+1

In testo_sostitutivo il nome di un parametro
preceduto da un # viene sostituito
dall’argomento racchiuso tra virgolette
#define eprint(expr) \
printf(#expr "=%g\n", expr)
la macro eprint(x/y) viene sostituita da:
printf("x/y" "=%g\n", x/y)
e le due stringhe vicine vengono concatenate
dal compilatore in "x/y=%g\n"
L’operatore di preprocessore ## concatena
due argomenti rimuovendo gli spazi intermedi
#define concat(uno,due) uno ## due
concat(nome,6) viene sostituito da nome6
13
Macro composte




14
Macro composte
Il corpo della macro (testo_sostitutivo) può
contenere più istruzioni C
#define swap(x,y) t=x; x=y; y=t;

La macro precedente viene chiamata come:
swap(a,b)

Per chiamarla come fosse una funzione
void, è sufficiente non indicare all’estrema
destra nella definizione il punto e virgola
swap(a,b);
 CON ‘;’

Perché la macro composta restituisca un valore
come una funzione, se possibile si separano le
istruzioni mediante virgole (dà come valore
quello dell’espr. più a destra)
#define maxswap(x,y) \
(t=x, x=y, y=t, x>y?x:y)
chiamata come: maxswap(a,b);
Se serve definire una variabile temporanea per
la macro, si crea un blocco con una coppia di
parentesi graffe:
#define swap(x,y) \
{ int t; t=x; x=y; y=t; }
Questa macro deve essere chiamata senza il ‘;’
swap(a,b)
15
Macro composte

Per poter chiamare anche la macro precedente
con il ‘;’ a fine istruzione così che sembri una
funzione, si scrivono le istruzioni all’interno di
un ciclo do-while fittizio (sempre falso, non
lo ripete mai) e privo del ‘;’ finale:
#define swap(x,y)
\
do {
\
int t; t=x; x=y; y=t; \
}while (0)
Questa macro deve essere chiamata con il ‘;’
finale:
swap(a,b);
16
Inclusione condizionale



Permette di include o escludere parte del
codice dalla compilazione e dal preprocessing
stesso
#if espressione_1
istruzioni
#elif espressione_2
istruzioni
...
#else
istruzioni
#endif
Solo uno dei gruppi di istruzioni sarà elaborato
dal preprocessore e poi compilato
17
Inclusione condizionale




18
Inclusione condizionale
Le espressioni devono essere costanti intere
(non possono contenere sizeof(), cast o
costanti enum), sono considerate vere se !=0
L’espressione defined(nome) produce
1 se nome è stato definito (con #define),
0 altrimenti
#ifdef nome equivale a:
#if defined(nome)
e verifica che nome sia definito
#ifndef nome equivale a:
#if !defined(nome)
e verifica che nome non sia definito

Esempio
Per far compilare parti diverse a seconda del
sistema operativo, si può usare lo schema
seguente (il simbolo _WIN32 viene definito da
tutti i compilatori Win32):
#ifdef _WIN32
if (_oneexit(funz)==NULL)
abort();
#else
atexit(funz);
#endif
Sono due funzioni quasi equivalenti, la prima
esiste solo sui sistemi Win32, la seconda è
standard
19
Inclusione condizionale


Nel caso in cui un file incluso ne includa a sua
volta altri, per evitare di includere più volte lo
stesso file, si può usare lo schema seguente
(quello che segue è il file hdr.h):
#ifndef HDR
#define HDR
... contenuto di <hdr.h>
#endif
Se venisse incluso una seconda volta, il
simbolo HDR sarebbe già definito e il
contenuto non verrebbe nuovamente incluso
nella compilazione
20
Inclusione condizionale


Per escludere dalla compilazione un grosso
blocco di codice (anche con commenti):
#if 0
codice da non eseguire
#endif
Per isolare istruzioni da usare solo per il
debug:
#ifdef DEBUG
printf("Valore di x: %d\n", x);
#endif
La #define DEBUG viene eliminata a
programma ultimato (spesso il simbolo DEBUG
viene definito dal compilatore stesso se in
configurazione debug)
21
Macro predefinite

Macro predefinite
Sono simboli definiti dal preprocessore:

__DATE__ Data di compilazione del file sorgente nel
formato di asctime() (in <time.h>)
__FILE__ Nome del file corrente tra virgolette
__LINE__ Numero di linea della riga corrente nel file
sorgente, può essere alterato da #line
__STDC__ Indica che il compilatore è completamente
aderente allo standard ANSI e non
fornisce alcuna estensione
__TIME__ Ora di compilazione del file sorgente nella
forma hh:mm:ss.
__TIMESTAMP__ Data e ora di compilazione del file
23
Esercizi
1.
2.
3.
22
Scrivere una macro swap(t,x,y) che
scambi il valore di due argomenti di tipo t.
Scrivere una macro printArray(v,n) che
visualizzi il contenuto del vettore v di
lunghezza n.
Scrivere una macro sumArray(v,n,sum)
che sommi gli n valori del vettore v e metta il
risultato in sum.


La direttiva #line può essere seguita da un
numero intero per impostare il valore di
__LINE__ della riga corrente del codice C, la
numerazione delle righe successive prosegue
da quel valore
#line 100
La direttiva #line può inoltre impostare un
nuovo nome di file
#line 100 "file1.c"
Il valore di __LINE__ e il nome del file
vengono usati nelle informazioni di
diagnostica fornite dal compilatore

Documenti analoghi

I Fondamenti

I Fondamenti • Consente al programmatore di controllare la compilazione del codice del programma. • Le direttive condizionali del preprocessore vengono valutate come espressioni costanti intere. • Esempio: #def...

Dettagli

lezione 12 - Direttive del preprocessore

lezione 12 - Direttive del preprocessore Direttive condizionali di compilazione La direttiva #elif significa “else if” ed è usata per stabilire una catena di “if-else-if” che realizza una compilazione condizionale multipla. Se l’espressio...

Dettagli