DICHIARAZIONI DI FUNZIONE La dichiarazione di una funzione è costituita dalla sola interfaccia, senza corpo (sostituito da un ;) <dichiarazione-di-funzione> ::= <tipoValore> <nome>(<parametri>) ; DICHIARAZIONI DI FUNZIONE • Perché introdurre la dichiarazione di funzione? • Non bastava la definizione..???!? DICHIARAZIONI DI FUNZIONE Osservazione cruciale: per usare un componente software • non c’è bisogno di sapere COME È FATTO • basta sapere COME SI USA, cioè - nel caso di una funzione come va invocata. DICHIARAZIONI DI FUNZIONE Dunque, • non occorre conoscere tutta la definizione • basta conoscere la dichiarazione, perché essa specifica proprio il contratto di servizio! DICHIARAZIONI DI FUNZIONE • La definizione di una funzione costituisce l’effettiva realizzazione del componente • La dichiarazione specifica il contratto di servizio fra cliente e servitore, esprimendo le proprietà essenziali della funzione. DICHIARAZIONI DI FUNZIONE • La definizione di una funzione Dice come è fatto il costituisce l’effettiva realizzazione componente del componente • La dichiarazione specifica il contratto di servizio fra cliente e servitore, esprimendo le proprietà essenziali della funzione. DICHIARAZIONI DI FUNZIONE • La definizione di una funzione costituisce l’effettiva realizzazione del componente Dice come si usa il componente • La dichiarazione specifica il contratto di servizio fra cliente e servitore, Per usare una funzione esprimendo le proprietà non è essenziali necessario della funzione. sapere come è fatta! Anzi… è controproducente!! DICHIARAZIONI DI FUNZIONE • La dichiarazione specifica: – il nome della funzione – numero e tipo dei parametri (non necessariamente il nome) – il tipo del risultato interfaccia DICHIARAZIONI DI FUNZIONE • La dichiarazione specifica: – il nome della funzione – numero e tipo dei parametri (non necessariamente il nome) interfaccia – il tipo del risultato Il nome dei parametri non è necessario: può esserci, ma viene ignorato DI FUNZIONE Non deveDICHIARAZIONI stupire: esso avrebbe significato solo • La dichiarazione specifica: nell’environment della funzione, che qui non c’è! – il nome della funzione – numero e tipo dei parametri (non necessariamente il nome) interfaccia – il tipo del risultato Il nome dei parametri non è necessario: può esserci, ma viene ignorato DEFINIZIONE vs. DICHIARAZIONE • La definizione di una funzione costituisce l’effettiva realizzazione del componente Non può essere duplicata! DEFINIZIONE vs. DICHIARAZIONE • La definizione di una funzione costituisce l’effettiva realizzazione del componente Non può essere duplicata! Una applicazione deve contenere una e una sola definizione per ogni funzione utilizzata DEFINIZIONE vs. DICHIARAZIONE • La definizione di una funzione costituisce l’effettiva realizzazione del componente Non può essere duplicata! La compilazione di una definizione genera il codice corrispondente alla funzione. Una applicazione deve contenere una e una sola definizione per ogni funzione utilizzata DEFINIZIONE vs. DICHIARAZIONE • La dichiarazione di una funzione costituisce solo una specifica delle proprietà del componente Può benissimo essere duplicata senza danni DEFINIZIONE vs. DICHIARAZIONE • La dichiarazione di una funzione costituisce solo una specifica delle proprietà del componente Può benissimo essere duplicata senza danni Una applicazione può contenerne più di una DEFINIZIONE vs. DICHIARAZIONE • La dichiarazione di una funzione costituisce solo una specifica delle proprietà del componente La compilazione di una dichiarazione non genera un solo byte di codice Può benissimo essere duplicata senza danni Una applicazione può contenerne più di una DEFINIZIONE vs. DICHIARAZIONE • La definizione è molto più di una dichiarazione – definizione = dichiarazione + corpo Per questo, la definizione funge anche da dichiarazione (non viceversa!) FUNZIONI & FILE • Un programma C è, in prima battuta, una collezione di funzioni – una delle quali è il main • Il testo del programma deve essere scritto in un file di testo – un concetto del sistema operativo, non del linguaggio C Quali regole osservare ? FUNZIONI & FILE • Il main può essere scritto dove si vuole nel file – viene chiamato dal sistema operativo • Una funzione, invece, deve rispettare una regola fondamentale di visibilità – prima che qualcuno possa chiamarla, la funzione deve essere stata dichiarata – altrimenti, si ha errore di compilazione. File prova1.c int fact(int); ESEMPIO main() { int y = fact(3); } Dichiarazione: deve precedere l’uso Uso (chiamata) int fact(int n) { return (n<=1) : 1 : n*fact(n-1); } Definizione FUNZIONI & FILE • Il main può essere scritto dove si Caso particolare: poiché la definizione vuole nel file da dichiarazione, la funge anche – viene dal se sistema operativo regolachiamato è rispettata la definizione appare prima invece, della chiamata • Una funzione, deve rispettare una regola fondamentale di visibilità – prima che qualcuno possa chiamarla, la funzione deve essere stata dichiarata – altrimenti, si ha errore di compilazione. ESEMPIO File prova2.c int fact(int n) { return (n<=1) : 1 : n*fact(n-1); } Definizione: funge anmain() { che da dichiarazione se precede l’uso int y = fact(3); } Uso (chiamata) FUNZIONI E VARIABILI LOCALI • Il corpo di una funzione costituisce un blocco del quale i parametri della funzione sono considerati variabili locali • In C e Java, come in molti altri linguaggi, è permessa la definizione di nuove variabili entro il corpo di una funzione VARIABILI LESSICALI vs. LIBERE • Le variabili la cui definizione compare nell’ambito del blocco costituito dal corpo della funzione (inclusi i parametri) vengono chiamate variabili lessicali. • Le variabili non definite entro il blocco della funzione (e diverse dai parametri) vengono chiamate variabili libere. ESEMPIO Variabili lessicali float fahrToCelsius(float F){ float temp = 5.0 / 9; return temp*(F - trentadue); } Variabile libera FUNZIONI & SCOPE RULES Gli identificatori dichiarati dentro una funzione (parametri formali inclusi) sono visibili SOLO all'interno della funzione Gli identificatori dichiarati nel blocco in cui la definizione della funzione è inserita sono visibili ANCHE dentro la funzione (a meno che non siano ridefiniti) ESEMPIO int trentadue = 32; float fahrToCelsius( float F ){ float temp = 5.0 / 9; return temp * ( F - trentadue ); } Variabile definita nel blocco in cui è inserita la definizione della funzione VARIABILI LIBERE & COMPONENTI SOFTWARE ATTENZIONE! Le variabili libere: introducono un accoppiamento fra cliente e servitore condizionano l’utilizzo della funzione all’esistenza di una definizione “esterna” per la variabile libera limitano la riutilizzabilità della funzione intesa come componente software. ESEMPIO int trentadue = 32; float fahrToCelsius( float F ){ float temp = 5.0 / 9; return temp * ( F - trentadue ); In C, una variabile definita } fuori da una funzione si dice variabile globale In effetti, il suo environment di definizione ESEMPIO non coincide con quello di nessuna funzione (neppure con quello del main) int trentadue = 32; float fahrToCelsius( float F ){ float temp = 5.0 / 9; return temp * ( F - trentadue ); In C, una variabile definita } fuori da una funzione si dice variabile globale VARIABILI GLOBALI • Una variabile globale è definita fuori da qualunque funzione (“a livello esterno”) • tempo di vita = intero programma • scope = il file in cui è dichiarata (dal punto in cui è scritta in avanti) ESEMPIO int trentadue = 32; float fahrToCelsius( float F ){ float temp = 5.0 / 9; return temp * ( F - trentadue ); } Definizione (e inizializzazione) di una variabile globale VARIABILI: DICHIARAZIONI E DEFINIZIONI • Anche per le variabile globali si distingue fra dichiarazione e definizione – al solito, la dichiarazione esprime proprietà associate al simbolo, ma non genera un solo byte di codice – la definizione invece implica anche allocazione di memoria VARIABILI: DICHIARAZIONI E DEFINIZIONI • Regola fondamentale di visibilità: – prima dell’uso, una variabile globale deve almeno essere stata dichiarata – Caso particolare: la definizione può fungere anche da dichiarazione ESEMPIO File prova3.c Definizione (e iniint trentadue = 32; zializzazione) della float fahrToCelsius(float); variabile globale main() { float c = fahrToCelsius(86); } float fahrToCelsius(float f) { return 5.0/9 * (f-trentadue); } Uso della var.glob. VARIABILI: DICHIARAZIONI E DEFINIZIONI • Come distinguere la dichiarazione da una definizione? – non c’è l’analogo del corpo della funzione.. • Si usa la parola chiave extern – int trentadue = 10; è una definizione (con inizializzazione) – extern int trentadue; è una dichiarazione ESEMPIO File prova4.c extern int trentadue; Dichiarazione variabile globale float fahrToCelsius(float f) { return 5.0/9 * (f-trentadue); } Uso della var.glob. main() { float c = fahrToCelsius(86); } Definizione della int trentadue = 32; variabile globale VARIABILI: DICHIARAZIONI E DEFINIZIONI • Come sempre, una applicazione può contenere – più dichiarazioni per lo stesso simbolo – ma una e una sola definizione di tale simbolo. • NB: la dichiarazione non può contenere anche una inizializzazione – è solo un avviso, la variabile non è lì! FUNZIONI come COMPONENTI SW • Una funzione è un componente software • che ha un suo environment di definizione • e può essere invocato per nome da altre funzioni (in particolare, dal main) Dove scrivere le funzioni? Quali regole pratiche? Quale organizzazione? COSTRUZIONE DI UN’APPLICAZIONE Per costruire un’applicazione occorre • compilare il file (o i file se più d’uno) che contengono il testo del programma (file sorgente) Il risultato sono uno o più file oggetto. • collegare i file oggetto l’uno con l’altro e con le librerie di sistema. COMPILAZIONE DI UN’APPLICAZIONE 1) Compilare il file (o i file se più d’uno) che contengono il testo del programma – File sorgente: estensione .c – File oggetto: estensione .o o .obj f1.c f1.obj compilatore COMPILAZIONE DI UN’APPLICAZIONE 1) Compilare il file (o i file se più d’uno) Una che però che contengono il versione testo deltradotta programma non è autonoma (e, quindi, – File sorgente: estensione .c non è direttamente eseguibile). – File oggetto: estensione .o o .obj f1.c f1.obj compilatore COLLEGAMENTO DI UN’APPLICAZIONE 2) Collegare il file (o i file) oggetto fra loro e con le librerie di sistema – File oggetto: estensione .o o .obj – File eseguibile: estensione .exe o nessuna f1.obj linker prog.exe LIBRERIE DI SISTEMA Programma eseguibile COSTRUZIONE DI UN’APPLICAZIONE Perché tutto questo? • il nostro programma non è destinato a funzionare sulla macchina "nuda", ma sulla macchina rivestita dal sistema operativo • quindi il nostro programma, per funzionare, deve necessariamente interfacciarsi col sistema operativo stesso. COSTRUZIONE DI UN’APPLICAZIONE Non solo: • alcune istruzioni “complesse” del linguaggio potrebbero essere realizzate in realtà da "mini-programmi" forniti insieme al compilatore, che li "ingloba" quando occorre. LIBRERIE DI SISTEMA: insieme di componenti software che consentono di interfacciarsi col sistema operativo, usare le risorse da esso gestite, e realizzare alcune "istruzioni complesse" del linguaggio COSTRUZIONE DI UN’APPLICAZIONE Last but not least... • una applicazione complessa non può essere sviluppata in un unico file: sarebbe ingestibile! • deve necessariamente essere strutturata su più file sorgenti, che devono poter essere compilati separatamente • per essere poi fusi insieme alla fine. COSTRUZIONE “MANUALE” In passato, la costruzione si faceva “a mano”, attivando compilatore e linker dalla linea di comando del sistema operativo (DOS, Unix, ...) C:\PROVA> gcc -c f1.c (genera f1.obj) C:\PROVA> ld -o prog.exe f1.obj –lc (genera prog.exe) Eseguibile da produrre File oggetto Libreria C AMBIENTI INTEGRATI Oggi, gli ambienti di lavoro integrati automatizzano la procedura: compilano i file sorgente (se e quando necessario) invocano il linker per costruire l’eseguibile ma per farlo devono sapere: quali file sorgente costituiscono l’applicazione il nome dell’eseguibile da produrre. PROGETTI È da queste esigenze che nasce il concetto di PROGETTO • un contenitore concettuale (e fisico) • che elenca i file sorgente in cui l’applicazione è strutturata • ed eventualmente altre informazioni utili. Oggi, tutti gli ambienti di sviluppo integrati, per qualunque linguaggio, forniscono questo concetto e lo supportano con idonei strumenti. PROGETTI IN DJGPP/RHide Si sceglie la voce di menù Project Open Project, e, nella finestra che appare, si sceglie un nome per il nuovo progetto (che sarà anche il nome dell’eseguibile) PROGETTI IN DJGPP/RHide Si va poi nella Project Window che appare, e si preme sulla tastiera il tasto INS (o si sceglie da menù l’opzione Project add item) PROGETTI IN DJGPP/RHide Dalla finestra Add item che appare si selezionano i file sorgente che si vogliono inserire nel progetto. PROGETTI IN DJGPP/RHide Facendo doppio clic su un nome di file (ad esempio, f1.c) si apre l’editor per modificarlo. PROGETTI IN DJGPP/RHide A questo punto: per compilare, Compile Compile (ALT+F9) (ricompila solo i file modificati dall’ultima compilazione) per collegare, Compile Link PROGETTI IN DJGPP/RHide Oppure le due fasi insieme: Compile Make (F9) A questo punto: per compilare, Compile Compile (ALT+F9) (ricompila solo i file modificati dall’ultima compilazione) per collegare, Compile Link PROGETTI IN TURBO C Dal sceglie il menù Project New Project, (oppure l’icona in alto)... PROGETTI IN TURBO C La finestra che appare richiede varie informazioni. 1) percorso e nome del progetto (per default coincide con il Target Name, cioè col nome dell’eseguibile da produrre) PROGETTI IN TURBO C 2) il Target Type, cioè il tipo di applicazione da generare: essenziale scegliere EasyWin (non Application) Turbo C è un ambiente multi-target, cioè può produrre sia applicazioni per Windows (che vanno strutturate in modo particolare), sia applicazioni standard da eseguire in una finestra di Windows emulando una console. 3) premendo il tasto PROGETTI IN TURBO C Advanced: un nodo iniziale .c nessuna risorsa (né .rc, né .def) PROGETTI IN TURBO C Si va poi nella finestra Project che appare, e si preme sulla tastiera il tasto INS (o si fa clic nella finestra con il tasto destro e si sceglie la voce Add Node) PROGETTI IN TURBO C Dalla finestra Add to Project List si selezionano i file da inserire nel progetto. PROGETTI IN TURBO C Facendo doppio clic su un nome di file (ad esempio, aaa.c) si apre l’editor per modificarlo. PROGETTI IN TURBO C A questo punto: per compilare, Project Compile (ALT+F9) (ricompila solo i file modificati dall’ultima compilazione) per collegare, Project Make all (F9) (fa le due fasi insieme; non si può solo collegare) ESEGUIRE IL PROGRAMMA Una volta scritto, compilato e collegato il programma (ossia, costruito l’eseguibile) Come si esegue il programma? Come “spiare” quello che fa? Occorre uno strumento che consenta di • eseguire il programma passo per passo • vedendo le variabili e la loro evoluzione • e seguendo le funzioni via via chiamate. ESEGUIRE IL PROGRAMMA Una volta scritto, compilato e collegato il programma (ossia, costruito l’eseguibile) Come si esegue il programma? Come “spiare” quello che fa? Occorre uno strumento che consenta di IL DEBUGGER • eseguire il programma passo per passo • vedendo le variabili e la loro evoluzione • e seguendo le funzioni via via chiamate. ESEGUIRE IL PROGRAMMA Sia Rhide sia TURBO C incorporano un debugger con cui eseguire il programma • riga per riga – entrando anche dentro alle funzioni chiamate (Run trace into; F7) (Debug trace into; F7) – oppure considerando le chiamate di funzione come una singola operazione (Run step over; F8) (Debug step over; F8) • oppure fino alla riga desiderata (Run Go to cursor; F4) (F4) ESEGUIRE IL PROGRAMMA … e con cui inoltre è possibile: • controllare istante per istante quanto vale una variabile – Debug Watch an expression (CTRL+F7) – Debug Add watch (CTRL+F5) • vedere istante per istante le funzioni attive (record di attivazione nello stack) – Debug Call stack (CTRL+F3) – View Call stack UN ESEMPIO COMPLETO: IL FATTORIALE Project: c:\temp\fatt Target Name: fatt Target Type: EasyWin ESEMPIO: IL FATTORIALE Source node: fatt.c ESEMPIO: IL FATTORIALE Si scrive il programma, lo si salva, e... ESEMPIO: IL FATTORIALE … lo si costruisce (F9). Se non ci sono errori... ESEMPIO: IL FATTORIALE … si può passare a eseguirlo con il debugger. Premendo F7 ci si posiziona all’inizio del main. ESEMPIO: IL FATTORIALE Premendo ancora F7 ci si posiziona sulla prima istruzione, pronti per eseguirla. ESEMPIO: IL FATTORIALE Volendo, si può mettere sotto osservazione la variabile y, sfruttando l’Add watch. Inoltre, si possono vedere i record di attivazione con il View Call Stack (ora, ovviamente, c’è solo il main) ESEMPIO: IL FATTORIALE Via via che l’esecuzione prosegue, il Call Stack mostra la successione dei record di attivazione che si accumulano. Si noti che la variabile y risulta ora indefinita: siamo usciti dallo scope del main! Però, possiamo aggiungere ai watch la variabile v, che esiste nello scope di factIter. ESEMPIO: IL FATTORIALE Nel momento di massima espansione (che coincide col momento in cui si ha il risultato), il Call Stack mostra ben 5 record attivi. Si noti che v, come previsto, vale attualmente 6 (che costituisce il risultato) ESEMPIO: IL FATTORIALE Appena prima che il main termini, y vale finalmente 6. La computazione è completata. ESEMPIO: IL FATTORIALE Quando il main è terminato, nessuna variabile esiste più Tutto è scomparso.