Compilatori ed Interpreti

Giorgio Borelli

Scelta di un Compilatore a riga di comando

Quando si realizza un software, il programmatore detta le istruzioni al computer tramite la scrittura di un codice sorgente in un determinato linguaggio di programmazione. Il codice sorgente però non è direttamente eseguibile dalla CPU (Central Processing Unit), è necessario “tradurlo” in linguaggio macchina, ossia un file binario contenente microistruzioni gestibili dal processore. Inoltre diverse architetture di processori prevedono linguaggi macchina differenti.

La traduzione viene compiuta in maniera del tutto automatica, con opportuni programmi detti, Compilatori ed Interpreti, ognuno con le proprie caratteristiche, vantaggi e svantaggi. A seconda di come il linguaggio di programmazione viene tradotto nel linguaggio macchina, si parla dunque di linguaggi compilati e linguaggi interpretati.

Andiamo quindi a scoprire le principali differenze tra linguaggi compilati ed interpretati, quali processi seguono per generare il codice macchina sino ad arrivare alla creazione dell'eseguibile.

Linguaggi Compilati

I compilatori leggono il programma sorgente e lo traducono in linguaggio macchina generando un programma oggetto. La fase di compilazione è accompagnata dalla rilevazione degli errori. Vediamo tutte le fasi di un processo di traduzione:

  • - Preprocessing: semplici operazioni di editing automatiche (rimozione commenti e espansione di definizioni) e inclusione di file.
  • - Analisi lessicale (scanning): verifica le regole di aggregazione dei caratteri dell’alfabeto in simboli del linguaggio (verifica identificatori e simboli).
  • - Analisi sintattica (parsing): verifica la correttezza del concatenamento delle parole del linguaggio per formare “frasi”, riordinando le operazioni in una struttura gerarchica, solitamente ad albero.
  • - Analisi semantica: determina la compatibilità dei tipi, dei parametri delle funzioni ed il significato da attribuire ad ogni “frase”.

Il programma oggetto però non è ancora pronto per essere eseguito dalla macchina, è necessario produrre un file eseguibile. Al fine di ottenere tale file (*.exe) è necessario collegare (linker) tra loro i diversi file oggetto e le librerie di funzioni richiamate nel programma. Le librerie di funzioni sono raccolte di piccoli programmi oggetto che forniscono particolari funzionalità: funzioni matematiche, chiamate al sistema (apertura di un file, creazione di una finestra) e così via. Il processo di traduzione e creazione di un eseguibile è illustrato dalle figure seguenti:

 

Processo per la creazione di un file eseguibile

 

 

Processo per la creazione di un file exe

Oltre alle librerie di funzioni possiamo avere anche delle librerie dinamiche (ad es. in Windows le DLL), queste vengono caricate in memoria solo quando necessarie al file eseguibile, con il conseguente vantaggio di risparmiare memoria non dovendo caricare tutti i moduli del sistema e non dovendo ricompilare i file eseguibili dopo l’aggiornamento delle librerie.

Tipici esempi di linguaggi compilati sono il linguaggio C ed il linguggio C++, il primo è procedurale il secondo è ad oggetti.

File Eseguibili

Gli strumenti dello sviluppatore di un software sono un insieme di programmi quali: editor, traduttore, linker, debugger. Tali strumenti consentono la scrittura, la verifica e l’esecuzione di nuovi programmi per applicazioni specifiche. Il risultato dell’attività di sviluppo di un programma è il file eseguibile, un file binario che viene caricato in memoria e svolge i compiti per cui è stato progettato. Vediamo per meglio capire il funzionamento dell’eseguibile in quali aree viene suddivisa la memoria:

  • - Area codice: contiene il codice (come microistruzioni della CPU) del programma
  • - Area dati: contiene le variabili globali e statiche
  • - Area heap: disponibile per allocazioni dinamiche
  • - Area stack: disponibile per allocazioni statiche; contiene i record di attivazione delle funzioni (tutti i dati necessari alla chiamata, esecuzione e ritorno di una funzione)
  • 1. la CPU ha un set di microistruzioni differenti
  • 2. il S.O. fornisce chiamate a sistema differenti (anche con la stessa CPU)

Un file eseguibile è specifico per una particolare architettura hardware e software, pertanto può risultare non utilizzabile per due ragioni: Il file eseguibile come detto può essere costruito tramite uno o più moduli oggetto, questi devono contenere una sola funzione main() che richiama tutte le altre (se una funzione è stata richiamata ma non linkata, il linker genererà un errore).

 

Linguaggi Interpretati

Gli interpreti non sono altro che dei programmi che svolgono un ruolo analogo a quello dei compilatori, a differenza di questi ultimi però, il codice sorgente viene tradotto ed eseguito istruzione per istruzione, traducono cioè il programma sorgente sequenzialmente, dall’inizio, alternando la fase di traduzione di ciascuna istruzione all’esecuzione effettiva della stessa ( tipici linguaggi interpretati sono il Basic e gli script delle shell ).

processo di traduzione di un codice sorgente interpretato

I maggiori vantaggi dei linguaggi interpretati sono la portabilità e la velocità di traduzione (quasi istantanea essendo fatta istruzione per istruzione), mentre il loro principale difetto è la lentezza di esecuzione (molto più lento dell’equivalente compilato).
Tali linguaggi sono adatti per realizzare programmi di piccole dimensioni e mirati a svolgere compiti specifici.

Tipici esempi di linguaggi interpretati sono il linguaggio Basic, il linguaggio Pascal o lo stesso Javascript ed in genere gli script delle shell o a riga di comando.

Linguaggi Pseudo-Compilati

In questi linguaggi il sorgente è pseudo-compilato in un codice simile all’assembler (P-code) e caricato in memoria, in seguito viene eseguito dall’interprete in maniera più veloce rispetto a quanto farebbe con l’equivalente codice sorgente di testo in formato ASCII. Le prestazioni di questi linguaggi sono eccellenti, in quanto non viene interpretata ogni linea del file sorgente, ma è simulata una “CPU generica”. Tuttavia non si ottengono prestazioni in esecuzione paragonabili a quelle ottenibile con la compilazione. Il vantaggio maggiore è la possibilità di avere funzionalità di livello più alto e portabilità. Un tipico esempio di linguaggio pseudo-compilato è MatLab.

Un caso particolare di tali linguaggi è Java. La differenza fondamentale risiede nel fatto che il risultato della pseudo-compilazione viene memorizzato in un file bytecode (*.class), il quale può essere trasferito su diversi sitemi, successivamente la sua interpretazione è realizzata da interpreti specifici per la piattaforma su cui è trasferito il P-code, detti JVM (Java Virtual Machine). Pertanto Java risulta utilissimo per realizzare applicazioni e software distribuiti su internet, alla quale accedono diverse tipologie di macchine con differenti sistemi.

Analogamente a Java allora possiamo definire come linguaggio pseudo-compilato anche i linguaggi che girano sulla piattaforma del .NET framework, come C# o VB.NET, per i quali non vengono compilati direttamente in linguaggio macchina, bensì in un codice intermedio, detto MSIL (Microsoft Intermediate Language) o più semplicemente IL, questo pseudo-linguaggio intermedio risulta indipendente dall'hardware sul quale dovrà essere eseguito, estendendo di fatto la portabilità dell'applicazione realizzata; così poi come avviene per i file .class di Java, i file Intermedi creati dal compilatore .NET vengono infine convertiti in codice macchina con degli opportuni JIT-ter (Just In Time - ter), ovvero dei compilatori appositi che ottimizzano il codice a seconda dell'Hardware sottostante; quindi avremo JIT-ter per architetture a 32 bit ed a 64 bit.

NOTA: sia per java che per il .net, dobbiamo mettere in risalto un'ulteriore importante differenza rispetto ai linguaggi compilati puri come il C++, i primi rispetto a quest'ultimo vengono eseguiti in un'ambiente protetto, il CLR (Common Language Runtime) per il .NET e la JVM per Java, che offrono all'applicazione in esecuzione una serie di servizi che la rendono più stabile, "veloce" ed efficiente, come es. possiamo citare il servizio svolto dal Garbage Collector, il quale gestisce opportunamente gli oggetti in memoria, scaricando il programmatore di questo gravoso compito, che deve invece assolvere in toto nel linguaggio C++. Ecco che si effettua la distinzione tra linguaggi Managed come C# o Java contro linguaggi Unmanaged come il C++.

 

Conclusioni sui Linguaggi Compilati ed Interpretati

Concludendo questa breve panoramica sui linguaggi di programmazione, possiamo dire che non esiste un linguaggio ottimo in senso assoluto, bisogna tenere conto dell’ambito e del contesto in cui si lavora e scegliere il linguaggio da utilizzare caso per caso. Proviamo comunque fare un breve elenco dei linguaggi più diffusi e del loro ambito d'applicazione, quali:

  • - Programmazione di sistema, calcolo numerico, realizzazione di driver: Linguaggio C
  • - Calcolo numerico, grafica e applicazioni scientifiche. Ambiente MatLab
  • - Applicazioni Desktop: Visual Basic.NET, C#, C++
  • - Programmazione Web: Java, ASP.NET, PHP

ovviamente l'elenco è riduttivo, invito i lettori pertanto a dire in quale linguggio programmano, se è un linguaggio interpretato o compilato? E in quale ambito lo utilizzate? Dite la vostra attraverso i commenti, ogni contributo sarà ben accetto.

Categorie: .NET framework | Programmazione

Tags: ,

Pingbacks and trackbacks (1)+

Aggiungi Commento

biuquote
Loading