|
prof. Nunzio Brugaletta |
Introduzione alluso di Borland C++Builder |
EnneBi - Programmazione
Il processo
di compilazione - Ambienti integrati e C++ Builder
- Applicazioni console su singolo file - Alcuni
errori comuni - Esecuzione del programma - Il
debugging dei programmi - Utilizzo degli strumenti
di debugging -

Il processo di compilazione
Scelto un linguaggio di programmazione (per esempio C o C++) e codificato un programma, il problema che si pone è quello di fare diventare tale programma eseguibile da un computer. A tale scopo è necessario utilizzare un compilatore (nel nostro caso un compilatore C++) che è un particolare software che a partire da un programma scritto in un determinato linguaggio di programmazione (il programma sorgente C++) può generare un programma eseguibile. Tutto ciò è ottenuto dal compilatore traducendo in codice binario comprensibile dalla macchina le istruzioni contenute nel sorgente. Tale processo, che può variare e prevedere diverse fasi in dipendenza del compilatore stesso e del suo funzionamento, richiede dal programmatore lesecuzione di alcune operazioni che possono essere ricondotte in breve alle seguenti:
Scrittura
del sorgente e salvataggio su disco del sorgente stesso.
Tale operazione viene effettuata con lausilio di un editor:
software che permette appunto di inserire il testo con le istruzioni
del programma e salvarlo su memoria di massa. In questa fase il
programma è semplicemente una sequenza di caratteri priva di
significato per il computer e, quindi, il programma può
essere scritto utilizzando un qualsiasi programma che generi testo
puro. Non va bene, in genere, un programma di elaborazione testi
perché verrebbero inseriti nel testo anche i caratteri di
formattazione (grassetto, corsivo, dimensioni caratteri, colori,
ecc...) a meno che il software non preveda (e in genere è
possibile) la generazione di testo puro.
Compilazione
del sorgente. Tale fase
richiede appunto un compilatore che è in grado di
tradurre il sorgente in codici eseguibili per una determinata CPU
e per un determinato Sistema Operativo. Se il compilatore
non è in condizione di generare il codice eseguibile perché
è stato commesso qualche errore di sintassi, viene generata
la lista degli errori. In questo caso, anche se qualche compilatore
produce il codice eseguibile, non se ne può tenere conto
avendo, questo, comportamenti imprevedibili. Bisogna riprendere
leditazione del sorgente, correggere le istruzioni che hanno
generato errori e ripetere la compilazione con il programma
modificato.
Esecuzione
e test del programma. Una
volta generato il codice eseguibile è necessario verificare
se il programma fornisce i risultati attesi. A questo punto, scelti
i dati sui quali effettuare i test, si lancia il programma per tutti
i dati previsti dai test. Se i risultati prodotti sono quelli
aspettati: bene! In genere il caso più frequente è
quello in cui si verificano situazioni inattese: risultati non
coerenti rispetto alle attese, blocchi del programma. Si rende
necessario effettuare un monitoraggio dellesecuzione del
programma per vedere quali sono le situazioni che non permettono al
programma stesso di funzionare nella modalità corretta. In
questi casi è necessario utilizzare un debugger che è
un software che permette, per esempio, di controllare come variano,
nel corso dellesecuzione, i valori contenuti nelle variabili
utilizzate dal programma o fare eseguire le
singole istruzioni controllando una per una le modifiche
effettuate.
In definitiva, da quanto esposto, per completare il processo di compilazione per la produzione del codice eseguibile, è necessario utilizzare una serie di strumenti software.
Quanto meno bisogna utilizzare: editor, compilatore, debugger. In alcuni casi, per completare lopera del compilatore è necessario un linker, in altri casi, in dipendenza del tipo di compilatore, occorrono anche altri strumenti software, per esempio per lesecuzione del programma compilato.
Ambienti integrati e C++ Builder
Un ambiente di sviluppo integrato o IDE (Integrated Development Environment) è un software che racchiude, in un unico prodotto, tutto ciò che serve per sviluppare programmi. Utilizzando un ambiente integrato si può editare il sorgente, compilare, mandare in esecuzione, effettuare il debugging di un programma senza abbandonare lambiente di programmazione, con evidenti vantaggi dal punto di vista della rapidità di sviluppo.
Lo scopo di questi appunti è quello di fare prendere confidenza con C++ Builder della Borland (appunto un IDE) a cominciare dalla compilazioni di applicazioni su singolo file fino a scoprire gli strumenti per sviluppare applicazioni piùù complesse. La versione presa in considerazione è la 3, acclusa in versione completa e gratuita a CD che corredano alcune riviste in vendita nelle edicole, ma quanto verrà detto, vale anche per le versioni successive.
Una volta avviato, lIDE C++ Builder, si presenta in questo modo:

Non è necessario comprendere immediatamente a cosa servano tutti gli strumenti visualizzati. Mano a mano che saranno necessari, si cercherà di vedere in che modo possano essere utili.
Applicazioni console su singolo file
Si comincerà a utilizzare gli strumenti che mette a disposizione lIDE per sviluppare il più semplice tipo di applicazioni: le applicazioni console su singolo file.
Una applicazione console su singolo file è un programma, in cui il sorgente si può conservare in un singolo file fisico su disco, che comunica con lesterno utilizzando una interfaccia a caratteri (senza quindi lutilizzo di finestre, pulsanti, ).
Scegliendo dal menu File la voce New , viene mostrata una finestra di dialogo da cui scegliere il tipo di applicazione da sviluppare: nel nostro caso una applicazione console.

Confermare la scelta. Confermando la successiva finestra di dialogo (tasto Finish) senza alcuna variazione viene mostrata la finestra in cui editare il programma.

La finestra Object Inspector può essere chiusa. Nellesempio proposto non ci sono componenti da ispezionare: ci saranno quando si farà uso di componenti visuali (finestre, ecc ).
Ferme restando le caratteristiche standard di tutte le applicazioni (taglia, copia, incolla di parti di testo), lambiente di editazione del Builder è dotato di alcune caratteristiche interessanti:
Lindentazione
automatica. Caratteristica
molto comune negli editor per programmatori. Quando si va a nuova
riga, questa viene allineata alla riga precedente, quindi se, per
esempio il primo carattere della riga precedente cominciava 4
caratteri più a destra della precedente, anche questa nuova
riga comincerà nello stesso modo. Tale caratteristica mostra
la sua utilità maggiore durante linserimento delle
strutture di controllo: è praticamente impossibile
dimenticare qualche parentesi (errore molto comune).
La
colorazione delle strutture sintattiche.
I diversi costrutti sintattici sono colorati in modo diverso
rendendo semplice rintracciare eventuali errori di digitazione (ci
si accorge, per esempio, che una parola chiave non è scritta
correttamente perché si nota immediatamente il colore
diverso). La colorazione può essere personalizzata scegliendo
dal menu Tools la voce Environment Options e, quindi,
la linguetta Colors o scegliendo Properties dal menu
presentato facendo clic, con il tasto destro del mouse, su una zona
vuota delleditor.

Lintroduzione agevolata.
I costrutti sintattici più comuni (if, while,
) possono
essere introdotti automaticamente senza necessità di
digitazione. Basta portare il cursore nel punto in cui si vuole
introdurre il costrutto, premere la combinazione di tasti Ctrl+J e
scegliere (con un doppio clic del mouse), dal menu, il costrutto
desiderato.

Lasciando invariate le righe presentate in automatico dalleditor, si possono aggiungere le righe di codice del programma.
Nellesempio seguente è presentato un programma per il calcolo del quoziente fra due numeri interi. Nel programma sono stati volutamente inseriti alcuni degli errori che si presentano più frequentemente:

Una volta scritto il programma è opportuno, prima di mandarlo in esecuzione, salvarne una copia su disco. Questa operazione può, come comune a tutte le applicazioni, essere effettuata scegliendo la voce Save As dal menu File, la prima volta per assegnare il nome al file che contiene il sorgente del programma, scegliendo la voce Save, sempre dallo stesso menu, tutte le volte che si apportano modifiche e si vuole conservare una copia aggiornata.
Le stesse operazioni possono essere compiute premendo lapposito pulsante di scelta rapida dalla barra degli strumenti:

Conviene chiarire che quando si salva su disco il file contenete il sorgente del programma, se per esempio si è scelto il nome Quoziente, verranno salvati su disco due file fisici:
Quoziente.cpp:
il file contenete il sorgente del programma
Quoziente.bpr:
il file del progetto Quoziente. Di questo argomento, di cui
si tratterà più avanti, per il momento è bene
ricordare la presenza del file di progetto che praticamente è
il file principale a cui si farà riferimento. Per esempio
quando si carica da disco un programma esistente, si carica il file
di progetto.
Salvato su disco il programma (operazione che è sempre conveniente svolgere prima di lanciare lesecuzione del programma), selezionando la voce Run dellomonimo menu o premendo il pulsante corrispondente dalla barra delle applicazioni, il C++ Builder cerca di effettuare la traduzione del programma. Dopo qualche secondo, sotto la finestra delleditor, viene mostrata una nuova finestra con lelenco degli errori riscontrati:

Il cursore si posiziona nella riga del primo errore. In ogni caso, cliccando due volte sul messaggio di errore, si raggiunge subito la riga che può essere editata.
|
|
Il primo messaggio ci dice che il simbolo disv non è definito: il compilatore sta rilevando luso di un simbolo che non conosce. Effettivamente, fra le cose che conosce, è stata definita la variabile divs e non disv! (inversione lettere). Lavviso dellerrore viene evidenziato alla riga 14 perché è qui che, per la prima volta, si usa la variabile disv. In pratica è come se si usasse una variabile non dichiarata. Si fa notare che la colorazione della sintassi permette di evitare questo tipo di errori con le parole chiavi. Infatti se si fosse scritto itn al posto di int, questo non sarebbe visualizzato in grassetto come tutte le parole chiavi. |
|---|---|
|
|
Il secondo messaggio è un Warning (Avviso). Non si tratta di un vero e proprio errore ma di una situazione che potrebbe generare malfunzionamenti durante lesecuzione: se ci sono solo Warnings il programma viene compilato e mandato in esecuzione. In questo caso si è avvisati del fatto che si sta usando una variabile (il simbolo è conosciuto) senza che prima siano state effettuate operazioni di assegnazione. Infatti prima, sbagliando, si è effettuato linput su disv. |
|
|
Il terzo messaggio comunica che else non è posizionato correttamente. Se si esamina il listato si nota che qui, dimenticando di chiudere la parentesi graffa aperta nella riga della if, else è usato come se fosse una istruzione. In questo caso si può risalire facilmente al fatto di aver dimenticato una parentesi. In generale (si pensi, per esempio, ad un while), individuare dove si è dimenticata una parentesi, non è così immediato. |
|---|---|
|
|
Il quarto messaggio indica appunto che manca una parentesi. Si noti che tale mancanza viene rilevata alla fine del programma (che qui coincida con la fine della if, è puramente casuale). Solo ora, infatti, il compilatore può rendersi conto della non equivalenza fra il numero delle parentesi aperte e quello delle parentesi chiuse. Se nel programma sono utilizzate molte parentesi, rintracciare la posizione dove si è dimenticata la parentesi, può essere una operazione molto lunga. Per questo motivo, come si accennava in precedenza, è comune trovare negli editor per programmatori la funzionalità di indentazione automatica. Un uso oculato di tale caratteristica permette di evitare questo tipo di errori. |
Corretti gli errori e salvata su disco la nuova versione del programma, si può procedere con una nuova compilazione. Se non vengono rilevati errori, viene generata una console virtuale nella quale verrà fatto girare il programma. In pratica si apre una nuova finestra che simula lo schermo di un computer, che funziona con interfaccia a caratteri, e si potrà osservare laspetto che avrà tale computer durante lesecuzione del programma.

Lunica differenza fra la console virtuale e un computer reale consiste nel fatto che la console virtuale, una volta terminata lesecuzione del programma, viene immediatamente chiusa e, quindi, se, come nellesempio proposto, il programma termina effettuando un output, non si fa in tempo ad osservare il risultato prodotto. Per poter esaminare con calma i risultati è necessario bloccare la console. A tale scopo, per esempio, si possono effettuare sul sorgente del programma alcune aggiunte:
Includere
nel programma una nuova libreria contenete, fra laltro,
una funzione che blocca il programma fino alla digitazione di un
tasto qualsiasi:
#include <conio.h>
Inserire
alla fine del programma la funzione per ricevere un tasto qualsiasi:
getch();

Nella versione finale del programma (prima dellultima compilazione) le due nuove righe aggiunte possono essere eliminate.
La cartella dove sono conservati i file generati dallIDE, oltre a Quoziente.cpp e Quoziente.bpr, ora conterrà pure Quoziente.exe: il file eseguibile che può essere lanciato fuori dallambiente integrato.
Il compilatore, avendo il compito di tradurre in eseguibili le istruzioni contenute nel sorgente, è in grado di segnalare gli errori sintattici. Alcuni esempi di questo tipo di errori sono stati presi in considerazione precedentemente: omettere una parentesi, un punto e virgola, digitare in modo errato una parola chiave.
Gli errori sintattici sono, in definitiva, semplici da correggere perché il compilatore fornisce una indicazione delle sue aspettative e, in genere, anche una indicazione del punto in cui si trova lerrore: è necessario solo prendere un poco di confidenza con i messaggi generati dal compilatore, avere presente le regole sintattiche del linguaggio e la correzione dellerrore è una operazione, in genere, abbastanza agevole.
Esiste, invece, una categoria di errori che possono essere anche piuttosto difficili da individuare. Si tratta degli errori logici. In altri termini: il programma viene compilato regolarmente, ma durante lesecuzione, si evidenziano malfunzionamenti. Per esempio i risultati forniti in output differiscono da quelli attesi o, peggio ancora, i risultati non sono corretti solo in alcuni casi o, ancora, il programma si blocca in seguito ad errori determinati in sede di esecuzione (errori di run-time). Correggere gli errori logici del programma potrebbe sembrare una impresa quasi disperata: come si fa a sapere in che modo il computer genera i risultati visto che si possono vedere solo gli output finali?
Loperazione di ricerca e correzione degli errori logici è chiamata debugging, dal termine inglese bug (baco, insetto), e, quindi, effettuare il debugging di un programma vuol dire togliere i bachi ovvero trovare e correggere le istruzioni che non fanno sì che i risultati prodotti siano quelli attesi. I programmi che aiutano lo sviluppatore nella ricerca dei bug si chiamano appunto debugger. C++Builder mette a disposizione una serie di strumenti per il debugging dei programmi, tutti accessibili dal menu Run o dal menu contestuale visualizzato in seguito al clic del tasto destro del mouse nelleditor.
Fra gli strumenti più comuni utilizzati per il debugging si esamineranno:
Watch: punti di attenzione. Per esempio
si può voler fissare lattenzione su una determinata
variabile per vedere come cambia il valore in essa contenuto, in
conseguenza delle istruzioni eseguite. Praticamente si tratta di
guardare nella memoria del computer per osservare cosa avviene.
Breakpoint:
punti di stop. Si può bloccare lesecuzione del
programma in un determinato punto critico per esaminare, per
esempio, leffetto prodotto, nelle variabili, da una
determinata istruzione.
Trace:
seguire la traccia dellesecuzione. Si può osservare
lesecuzione delle istruzioni passo-passo per vedere, per
esempio, se vengono eseguite le istruzioni contenute in una if
o in un while.
Utilizzo degli strumenti di debugging
Riprendiamo da disco il programma Quoziente scritto in precedenza. A tale scopo si può procedere selezionando Open Project dal menu File o cliccando sul pulsante di scelta rapida della barra degli strumenti:

La prima azione che è opportuno compiere, allapertura di una sezione di debugging, è quella di abilitare la visualizzazione della Watch List, cioè la finestra dove sono visualizzate tutte le variabili soggette ad attenzione. Per la visualizzazione basta selezionare Watches dal menu View. La visualizzazione della finestra viene anche selezionata in automatico non appena, con uno dei modi che si esamineranno più avanti, viene fissato il primo punto di attenzione. Se si clicca col tasto destro del mouse nella finestra Watch List, si può abilitare, dal menu contestuale, lopzione Stay on Top. Ciò permette di vedere sempre in primo piano la finestra (tutte le altre finestre si vedranno sotto questa). In questo modo si avrà sempre facilmente visibile la finestra con le variabili e i valori in esse contenuti.
Per aggiungere la variabili, alla Watch List si può operare, per esempio, in uno dei due modi specificati appresso:
|
|
|
|---|---|
|
|
|
Si può ripetere uno dei procedimenti esaminati per ognuna delle variabili, o espressioni, che si vogliono tenere in osservazione, così come, se si vuole eliminare una variabile dalla Watch List, basta evidenziarla e premere il tasto Canc o scegliere dal menu contestuale, visualizzato col clic destro, la voce Delete Watch. Nel caso del programma Quoziente, si possono, per esempio, aggiungere alla Watch List le due variabili div e divs.
Si aggiunga adesso un breakpoint nella if di Quoziente. In questo modo, prima di eseguire la struttura di controllo, il programma si bloccherà permettendo lesame dei valori contenuti nelle variabili. Per aggiungere il breakpoint, portare il cursore allinizio della riga del programma che contiene la if e, dal menu contestuale mostrato al clic destro del mouse, scegliere Toggle Breakpoint. Tale opzione funziona da interruttore: se si sceglie una seconda volta, il breakpoint viene tolto.

Si può, ora, lanciare lesecuzione del programma ed introdurre gli input. Subito dopo linserimento del secondo input, il programma si blocca:

A questo punto, se per esempio i valori riscontrati non sono quelli attesi, si può bloccare in maniera definitiva lesecuzione del programma e tornare alleditor per apportare le opportune correzioni. Per interrompere il programma basta selezionare Program Reset dal menu Run.
Si può continuare lesecuzione del programma:
Scegliendo
Run to Cursor dal menu Run il programma continua
lesecuzione delle istruzioni dal punto in cui è stato
interrotto e fino allultima istruzione prevista o fino al
prossimo breakpoint.
Se
si vuole osservare la sequenza delle istruzioni eseguite, si può
eseguire il trace.
Per eseguire le istruzioni passo-passo si possono utilizzare i pulsanti di scelta rapida dalla barra degli strumenti:


Ora si può reiterare il procedimento esposto per osservare la sequenza delle operazioni effettivamente eseguite durante lelaborazione, uscire dalla sezione di debugging per correggere eventuali errori riscontrati, terminare lesecuzione del programma.
La pressione di uno qualsiasi dei due pulsanti della barra degli strumenti produce, nel caso del programma Quoziente, sempre gli stessi risultati. Il comportamento però differisce se listruzione da eseguire è la chiamata ad una funzione:
|
|
La pressione del tasto Trace into permette di passare allesecuzione della prossima istruzione. Listruzione successiva alla chiamata della funzione è la prima istruzione contenuta nella definizione della funzione, quindi, lesecuzione del programma si blocca alla prima istruzione della funzione. Si entra dentro la funzione. |
|---|---|
|
|
Anche la pressione del tasto Step over provoca il passaggio alla prossima istruzione eseguibile solo che, stavolta, la chiamata alla funzione è vista come singola istruzione. In altri termini lesecuzione delle singole istruzioni della funzione non viene tracciata, si passa direttamente alla istruzione successiva alla chiamata di funzione. Ovviamente gli effetti, delle istruzioni contenute nella definizione della funzione, saranno osservabili nella Watch List. Le istruzioni contenute nella definizione della funzione sono in ogni caso, come giusto, eseguite: solo levidenziazione dellesecuzione (trace) salta le istruzioni della funzione. |
Non essendoci, nel programma Quoziente, alcuna chiamata di funzione leffetto della pressione dei due tasti coinciderà e, quindi, diventa indifferente, nell'esempio proposto, quale dei due tasti si sceglie per effettuare il trace del programma.