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
• Consente al programmatore di controllare la
compilazione del codice del programma.
• Le direttive condizionali del preprocessore vengono
valutate come espressioni costanti intere.
• Esempio:
#def...
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...