Ambienti di sviluppo integrato Un ambiente di sviluppo integrato (IDE - Integrated Development Environment) è un ambiente software che assiste i programmatori nello sviluppo di programmi Esso è normalmente costituito da o un editor di codice sorgente o un compilatore (interprete) o un linker o un debugger o strumenti di utilità o editor di risorse o class brower o code profiler Laboratorio di Informatica Antonio Monteleone 9
Ambienti di sviluppo - compilatori Un compilatore è un programma che traduce (compila) una serie di istruzioni (codice sorgente) scritte in un linguaggio di programmazione (linguaggio sorgente) in istruzioni (codice oggetto) di un altro linguaggio (linguaggio oggetto o target). Normalmente un compilatore traduce istruzioni scritte in un linguaggio ad alto livello (C, C++, Pascal, BASIC, Java,...) in un linguaggio di più basso livello (assembly o linguaggio macchina) Laboratorio di Informatica Antonio Monteleone 10
Ambienti di sviluppo - compilatori Le fasi della compilazione analisi lessicale (lexer o scanner o tokenizer) pre-processamento (preprocessor) analisi sintattica (parser) analisi semantica generazione del codice ottimizzazione del codice (ottimizzatore) Laboratorio di Informatica Antonio Monteleone 11
Ambienti di sviluppo - compilatori Mediante l'analisi lessicale un compilatore suddivide il codice sorgente in una sequenza di unità lessicali dette token. Un token è un'unità atomica del linguaggio come parole chiave (while, for,...) identificatori (nomi di variabili, funzioni, classi, ) operatori (+, =, --,...) valori numerici a+12 viene tradotto in IDEN + NUM Laboratorio di Informatica Antonio Monteleone 12
Ambienti di sviluppo - compilatori La fase di preprocessamento consiste nella sostituzione di macro e nell'esecuzione di direttive di inclusione e compilazioni condizionali. Tipicamente la fase di preprocessamento precede l'analisi sintattica. #define MAX_VALUE 100 // MACRO #include <iostream> // direttiva di inclusione #ifdef _MSVC_ // comp.condizionale // compilato sse _MSVC_ è definito... #else... #endif Laboratorio di Informatica Antonio Monteleone 13
Ambienti di sviluppo - compilatori L'analisi sintattica esegue il controllo sintattico sulla sequenza di token generata nella fase di analisi lessicale. Il controllo sintattico è effettuato attraverso un grammatica che definisce la sintassi (regole sintattiche) del linguaggio di programmazione. Il risultato di questa fase è un albero di sintassi detto parse-tree. <Expression> ::= <Term> + <Expression> <Expression> ::= <Term> - <Expression> <Expression> ::= <Term> <Term> ::= <Factor> * <Term> <Term> ::= <Factor> / <Term> <Term> ::= <Factor> <Factor> ::= ( <Expression> ) <Factor> ::= <Identifier> ( <Expression> ) <Factor> ::= <Identifier> - <Factor> <Factor> ::= <Identifier> <Factor> ::= <Number> Laboratorio di Informatica Antonio Monteleone 14
Ambienti di sviluppo - compilatori L'analisi semantica controlla il significato (la semantica) delle istruzioni presenti nel codice in ingresso. Controlli tipici di questa fase sono ad esempio il type checking (controllo sui tipi), il definite assignment (controllo che tutte le variabili locali siano state inizializzate prima di essere utilizzate),... Laboratorio di Informatica Antonio Monteleone 15
Ambienti di sviluppo - compilatori Mediante la fase di ottimizzazione il codice in linguaggio intermedio è trasformato in forme equivalenti ma più veloci (o di dimensioni inferiori). Esempi di ottimizzazioni sono espansione di funzioni inline eliminazione di codice non raggiungibile trasformazione di cicli Return Value Optimization (RVO) Nella fase di generazione del codice il codice in linguaggio intermedio viene tradotto nel linguaggio oggetto, solitamente in linguaggio macchina. Laboratorio di Informatica Antonio Monteleone 16
Ambienti di sviluppo - compilatori E' possibile configurare il processo di compilazione (di generazione) mediante l'impostazione delle opzioni di compilazione (di link) definizione di macro (per impostare valori e controllare compilazioni condizionali) impostazione di directory di inclusione e di librerie (dove ricercare header file e librerie riferite nel programma) elenco delle librerie da utilizzare nella generazione del programma Laboratorio di Informatica Antonio Monteleone 17
Un programma C++ è costituito da uno o più file sorgente, ognuno dei quali contiene una porzione delle istruzioni del programma. Un file sorgente, insieme ai file che sono in esso inclusi mediante la direttiva #include del preprocessore, e privato delle sezioni di codice rimosse mediante direttive di compilazione condizionale, è chiamato unità di traduzione (compilazione) Unità di traduzione in C++ //foo.cpp #include <iostream> using namespace std; void bar(){ #ifdef WIN32 cout<< "WIN32 platform ; cout<< endl: #else cout << "Not a WIN32 platform" << endl: #endif } int main(){ bar(); } Laboratorio di Informatica Antonio Monteleone 18
Unità di traduzione in C++ I file sorgente che compongono un programma possono essere compilati in istanti diversi. un ambiente di sviluppo compila solo i file sorgente che sono stati modificati. si confronta la data di modifica del file sorgente (student.cpp) con la data del file oggetto ad esso corrispondente (student.obj) Se il file sorgente (student.cpp) è successivo al file oggetto (student.obj) (o se il file oggetto non esiste) il file sorgente è (ri-)compilato Le unità di traduzione compilate danno luogo a file oggetto separati (.obj,.o). E compito del linker collegare i file oggetto per generare un programma eseguibile o una libreria a collegamento dinamico. Laboratorio di Informatica Antonio Monteleone 19
Ambienti di sviluppo la fase di link Un linker è un programma che prende in ingresso uno o più file oggetto (generati dal compilatore) e li assembla (collega) in un singolo eseguibile o libreria Un file oggetto può contenere dei riferimenti a simboli (variabili, funzioni, classi) esterni, ossia non definiti all'interno del file oggetto. // main.cpp #include <iostream> int main(int argc, char *argv[]){ std::cout << "hello world"; return 0; } La variabile globale cout non è definita nel file oggetto corrispondente a main.cpp (main.obj). Tale file oggetto conterrà un riferimento al simbolo esterno cout e un segnaposto in corrispondenza dell'uso di cout E' compito del linker risolvere i riferimenti ai simboli esterni cercando tali simboli nei file oggetto (librerie) passatigli in input Se un simbolo esterno non viene trovato il linker genera un errore del tipo "simbolo esterno non risolto" (unresolved external symbol) Laboratorio di Informatica Antonio Monteleone 20
Ambienti di sviluppo - Il debug dei programmi Fare il debug di un programma consiste nel rimuoverne le anomalie (bug) mediante l analisi (interattiva) della sua esecuzione Gli strumenti cha assistono il programmatore nel debug di un programma sono detti debbuger Un debbuger esegue un programma tenendo traccia delle sue funzioni, istruzioni e variabili. Esso è solitamente fornito con l ambiente di sviluppo integrato (IDE, Integrated Development Environment) Un debugger può eseguire con successo un programma solo se questi ha associati i cosiddetti simboli di debug (debug symbols). Laboratorio di Informatica Antonio Monteleone 21
Ambienti di sviluppo - Il debug dei programmi Un debugger permette di o impostare punti di interruzione (breakpoint) o tracciare (trace) l esecuzione di un programma a partire dall entry point (main) o da un dato breakpoint o attraversare (risalire/scendere) lo stack delle chiamate a funzione (call-stack) o esaminare e cambiare il valore delle variabili (osservazione (watch) delle variabili) Laboratorio di Informatica Antonio Monteleone 22
Ambienti di sviluppo - Il debug dei programmi Un breakpoint definisce un punto di interruzione nell esecuzione di un programma. Quando il flusso di controllo raggiunge l istruzione su cui è stato impostato un breakpoint il debugger interrompe l esecuzione del programma. E così possibile esaminare lo stato del programma (valore delle variabili) in quel punto o tracciarne l esecuzione Tracciare (trace) l esecuzione di un programma consiste nell eseguirne le istruzioni passo-passo. o step over (step successivo) o step into (step interno) o step out (step esterno) Laboratorio di Informatica Antonio Monteleone 23
Ambienti di sviluppo - Il debug dei programmi Lo stack delle chiamate a funzione (call-stack) contiene la pila delle funzioni invocate dall inizio dell esecuzione del programma (main) fino all istruzione corrente. E possibile risalire nel call-stack per esaminare lo stato delle variabili in ciascuna delle funzioni invocate Un debugger permette di risalire all istruzione che ha generato un eccezione (access violation ) Viene riportato lo stack delle chiamate a funzione che hanno condotto all eccezione E compito del programmatore attraversare lo stack per analizzare le funzioni coinvolte e capire la causa dell eccezione Laboratorio di Informatica Antonio Monteleone 24
L uso di assert (1) Una asserzione specifica una condizione che ci si aspetta essere vera a un certo punto dell esecuzione di un programma. Il C++ (e il C) fornisce l istruzione (macro) assert per esprimere una asserzione assert valuta l espressione condizionale che in essa compare assert(i > 0); Se la condizione non è vera (vale false), l asserzione fallisce, l esecuzione del programma viene interrotta e compare un messaggio di assertion failed: <espressione>, file <file con l'errore>, line <linea di codice dell'assert> Laboratorio di Informatica Antonio Monteleone 25
L uso di assert (2) La macro assert è definita nell'header file <cassert> Una volta terminato il debug, non è necessario rimuovere tutte le occorrenze di assert che compaiono nel programma assert valuta l espressione condizionale solo in se il programma è stato compilato in modalità debug Per disabilitare le asserzioni è sufficiente definire la macro NDEBUG Con la direttiva #define NDEBUG del preprocessore prima dell inclusione di <cassert> Aggiungendo NDEBUG tra le opzioni del compilatore (-DNDEBUG) Laboratorio di Informatica Antonio Monteleone 26
L uso di assert (3) // this is a dummy example class foo { int elements[10]; public: foo(){/* do some initialization */} ; int getat(int index) const { assert(index>=0 && index<10); if(index>=0 && index<10) return elements[index]; return -1; // return a default value } }; Laboratorio di Informatica Antonio Monteleone 27