Linguaggi ad alto livello e le fasi del compilatore

Linguaggi ad alto livello e le fasi del compilatore

In informatica, sono centinaia i linguaggi ad alto livello sviluppati fino ad oggi, ma soltanto pochi di essi hanno conosciuto una grande popolarità ed utilizzo. Innanzitutto, il FORTRAN (FORmula TRANslator) fu sviluppato dall’IBM tra il 1954 e il 1957 per applicazioni scientifiche e tecniche, che richiedessero calcoli matematici complessi. Il FORTRAN è ancora utilizzato, specialmente nell’ambiente scientifico. Il COBOL (Common Business Oriented Language) fu sviluppato nel 1959 da costruttori di computer, industrie ed organismi governativi statunitensi. Il COBOL è utilizzato principalmente in applicazioni commerciali per le quali è necessaria la manipolazione efficiente di grandi quantità di dati. Ad oggi, più dei 50% delle applicazioni commerciali sono scritte in COBOL. Il Pascal fu progettato più o meno negli stessi anni del C dal professor Niklaus Wirth, soprattutto per usi accademici.

Nel corso degli anni ’60 il mercato del software ha conosciuto di fatto una notevole crisi: le commesse di software venivano evase generalmente in ritardo, i costi eccedevano di gran lunga i budget previsti e i prodotti finiti non erano totalmente affidabili. Ci si accorse, dunque, che la creazione di software era un’attività decisamente più complessa di quanto si pensava. L’attività di ricerca di quegli anni porto all’evoluzione della programmazione strutturata: un approccio disciplinato alla scrittura del software, realizzata con lo scopo di rendere i programmi più chiari e più semplici da verificare e correggere.

Uno dei risultati più tangibili di questa ricerca fu lo sviluppo del linguaggio di programmazione Pascal, ad opera di Niklaus Wirth nel 1971. Il Pascal, denominato così in onore al matematico del XVII secolo Blaise Pascal, fu espressamente progettato per insegnare la programmazione strutturata negli ambienti accademici, e divenne rapidamente il linguaggio di programmazione più utilizzato in molte Università. Purtroppo il Pascal non possiede molte delle caratteristiche necessarie nelle applicazioni commerciali ed industriali, quindi non ha conosciuto una grossa popolarità al di fuori dell’ambiente di Ricerca.

Il linguaggio Ada fu sviluppato nell’ambito di un progetto finanziato dai Dipartimento di Difesa degli Stati Uniti (DoD) tra la fine degli anni ’70 e l’inizio degli anni ’80. I sistemi di controllo complessi presenti presso il DoD erano stati sviluppati in numerosi linguaggi di programmazione; il DoD, però, era alla ricerca di un solo linguaggio capace di soddisfare tutte le sue esigenze. Scegliendo come base alcune caratteristiche del linguaggio Pascal, si sviluppò un linguaggio chiamato Ada (che, nei fatti, risulta piuttosto differente dal Pascal), in onore di Lady Ada Lovelace, figlia del poeta Lord Byron. Lady Lovelace, agli inizi del XIX secolo, scrisse il primo “programma” per calcolatore conosciuto. Esso avrebbe dovuto funzionare su di un calcolatore meccanico progettato da Charles Babbage (l’analytical engine). Una caratteristica degna di nota del linguaggio Ada è il supporto al cosiddetto multitasking, cioè l’esecuzione parallela di diverse attività. Gli altri linguaggi ad alto livello di uso comune, compresi il C e il C++, consentono invece ai programmatori di eseguire una sola attività alla volta.

Le fasi del compilatore per un linguaggio ad alto livello

Gli ambienti di sviluppo C++ generalmente sono formati da diverse componenti; un ambiente di sviluppo dei programmi, il linguaggio e la libreria standard del C++ (un tipico ambiente di sviluppo è illustrato nella figura seguente).

Prima di giungere all’esecuzione, un programma C++ attraversa sei fasi (come mostrato in figura): scrittura/modifica, preprocessing, compilazione, linking (collegamento), caricamento ed esecuzione. Nel seguito, ci riferiremo nelle nostre esemplificazioni ad un tipico sistema UNIX. In ogni caso, i programmi contenuti nel nostro libro potranno essere eseguiti, con nessuna o con minime modifiche, sulla maggior parte degli ambienti C++ esistenti compresi quelli disponibili sui sistemi operativi Microsoft. Se non state utilizzando un sistema UNIX fate riferimento ai manuali del vostro sistema o, eventualmente, chiedete al vostro sistemista di aiutarvi ad eseguire i programmi sul vostro sistema.

La prima fase è la scrittura/modifica di un file. Allo scopo si utilizza un programma detto editor che consente al programmatore di scrivere o modificare il suo programma. Il programma viene successivamente salvato su un’unità di memorizzazione secondaria, come per esempio il disco rigido, con la convenzione che i nomi dei file per i programmi C++ terminino con le estensioni .cpp, .cxx o .C (maiuscola). Consultate la documentazione del vostro ambiente di sviluppo per sapere che estensione utilizzare. Gli editor più comunemente utilizzati in ambiente UNIX sono vi e emacs. I pacchetti software come Borland C++ e Microsoft Visual C++ possiedono un proprio editor interno che si integra perfettamente con l’ambiente di programmazione. D’ora in avanti supporremo che sappiate utilizzare il vostro editor per scrivere i programmi.

Dopo avere scritto il programma è necessario compilarlo: il compilatore converte il codice del vostro programma C++ in linguaggio macchina (detto anche codice oggetto). In un ambiente C++ prima della fase di compilazione viene eseguita una pre-elaborazione del programma per mezzo di un componente software chiamato preprocessore. Il preprocessore C++ esegue delle istruzioni speciali dette direttive al preprocessore, le quali indicano alcune manipolazioni da effettuare sul codice prima della fase di compilazione. Tali manipolazioni consistono generalmente nell’inclusione di file esterni e nella sostituzione di stringhe all’interno del testo del programma. È dunque il compilatore che chiama il preprocessore prima di iniziare la compilazione.

La fase successiva è detta collegamento o linking. I programmi in C++ contengono spesso dei riferimenti a funzioni definite altrove, per esempio nelle librerie standard o in speciali librerie dedicate a particolari applicazioni. Il codice macchina prodotto dal compilatore (detto codice oggetto) contiene dunque dei riferimenti pendenti in corrispondenza di queste funzioni. Il linker lega il codice oggetto assieme con il codice delle funzioni mancanti per produrre quella che viene chiamata l’immagine eseguibile (o più semplicemente l’eseguibile), in cui non ci sono più riferimenti non risolti. Su un tipico sistema UNIX il comando per compilare ed effettuare il linking di un programma C++ è CC. Ad esempio, per compilare ed effettuare il linking del programma welcome .C digitate CC welcome .C al prompt dei comandi UNIX e premete il tasto Invio. Se la compilazione e il linking hanno successo verrà creato il file a . out; esso è l’eseguibile del nostro welcome .C.

La fase ancora successiva è quella di caricamento: prima che un programma possa andare in esecuzione deve essere portato in memoria. È il loader (caricatore) che prende l’immagine eseguibile e la trasferisce in memoria. Infine il computer, sotto il controllo della CPU, esegue il programma un’istruzione alla volta. Per caricare ed eseguire un programma in un sistema UNIX basta digitare a. out al prompt dei comandi e premere Invio.

Non sempre i programmi funzionano al primo tentativo: ciascuna delle fasi che abbiamo descritto può fallire a causa di diversi tipi di errori. Per esempio, un programma può tentare di dividere un numero per zero (operazione priva di significato in informatica come in matematica) e, in questo caso, il computer segnala un errore tramite un apposito messaggio. Il programmatore deve quindi ripercorrere tutte le fasi a partire da quella di scrittura/modifica per correggere l’errore e determinare se il programma modificato funzioni correttamente.

Linguaggi ad alto livello e le fasi del compilatore
Fasi del compilatore per un linguaggio ad alto livello (C++)

La maggior parte dei programmi riceve dei dati in input e ne invia altri in output. Alcune funzioni C++ prendono i dati in input dallo stream cin (nome del flusso di dati per l’input standard) che coincide normalmente con la tastiera; tuttavia cin può essere connesso ad altre unità. I dati sono quindi inviati in output sullo stream cout (flusso di dati per l’output standard) che coincide normalmente con lo schermo; allo stesso modo di cin, tuttavia, cout può essere connesso ad altre unità. Quando diciamo che un programma mostra un risultato, intendiamo generalmente che lo visualizza sullo schermo. I dati però possono anche essere inviati in output su altri dispositivi, come i dischi o le stampanti. Esiste anche uno stream di dati standard per gli errori denominato c err. Lo stream cerr, generalmente connesso allo schermo, viene utilizzato per visualizzare i messaggi di errore. Spesso i programmatori dirigono i dati destinati all’output, cioè a cout, a un’unità diversa dallo schermo, mantenendo l’associazione di c err con lo schermo, in modo da essere informati immediatamente di eventuali errori.

 

Precedente Internet Of Things (IoT) e le sue applicazioni Successivo Buone abitudini per scrivere codice in linguaggio C e C++

Lascia un commento

*