Il preprocessore del linguaggio C 1 Anno accademico 2010-2011 Sommario • Il preprocessore La sostituzione di macro Le compilazioni condizionali L’inclusione di file 2 Anno accademico 2010-2011 Introduzione 1 • Il preprocessore del linguaggio C è un programma a sé stante, che viene eseguito prima del compilatore, con propri lessico e sintassi, orientati alle linee • Le principali funzioni offerte dal preprocessore sono: Elaborazione di macro Inclusione di file sorgente Compilazione condizionale, che consente di compilare porzio- ni 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 3 Anno accademico 2010-2011 Introduzione 2 • Le direttive del preprocessore possono apparire ovunque nel codice sorgente (prima, dopo, o inframmezzate 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” 4 Anno accademico 2010-2011 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’utilizzo 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 Anno accademico 2010-2011 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 ) • Esempio: Corpo di macro Nome di macro ( Argomento di macro Sintassi di una macro di tipo funzione define MULT_BY_TWO(a) ((a)(a)) 6 Anno accademico 2010-2011 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 jMULT_BY_TWO(5); j10; 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 che il parametro venga istanziato correttamente all’atto dell’espansione della macro • 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 7 Anno accademico 2010-2011 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 8 Anno accademico 2010-2011 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]; La linea a cui viene fatto riferimento nel messaggio di errore è corretta! 9 Anno accademico 2010-2011 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 10 Anno accademico 2010-2011 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(jMAX; j>0; j) for(j100; j>0; j) L’assegnamento diviene un’espressione relazionale sintatticamente corretta, che il compilatore non evidenzia errore difficile da rilevare 11 Anno accademico 2010-2011 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 Anno accademico 2010-2011 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 13 Anno accademico 2010-2011 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 aa; } …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 MULT_BY_TO può ricevere un parametro a di tipo qualsiasi Anno accademico 2010-2011 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 chiamata di funzione viene compilata correttamente, con impredicibilità del comportamento in fase di esecuzione Per le macro, si ha sempre una segnalazione di errore in fase di compilazione 15 Anno accademico 2010-2011 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.) 16 Anno accademico 2010-2011 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) aa Se si passa alla macro un’espressione aritmetica… j 2SQUARE(34); 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 17 Anno accademico 2010-2011 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 18 Anno accademico 2010-2011 Macro vs Funzioni 1 • Sia macro che funzioni consentono di rappresentare con un singolo nome (alias) 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) 19 Anno accademico 2010-2011 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 20 Anno accademico 2010-2011 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 Anno accademico 2010-2011 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 Anno accademico 2010-2011 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 23 Anno accademico 2010-2011 Le compilazioni condizionali 4 • Le direttive if e endif controllano la compilazione delle istruzioni C racchiuse nel blocco condizionale, non la loro esecuzione if DEBUG if (exp_debug) { … … … } endif 24 Anno accademico 2010-2011 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 25 Anno accademico 2010-2011 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… if !FALSE define FALSE 0 endif Soluzione Anno accademico 2010-2011 FALSE viene comunque ridefinita se è stata definita come zero, mai altrimenti ifndef FALSE define FALSE 0 elif FALSE undef FALSE define FALSE 0 endif 26 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 includere file di definizione comuni, i file header, che possono essere condivisi da più file sorgente 27 Anno accademico 2010-2011 L’inclusione di file 2 • I file header sono caratterizzati dall’estensione .h e contengono i prototipi di funzione, 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 runtime prevedono un insieme di file header che occorre includere per poter richiamare le relative funzioni 28 Anno accademico 2010-2011