Questa è la prima parte dedicata ai linguaggi di programmazione a cui molto probabilmente eravate interessati se vi siete spinti fin qui.
Ora che avete nozioni sufficenti su cos’è la programmazione e cos’è un algoritmo non dovrebbe essere difficile spiegare in cosa consiste un linguaggio di programmazione.
In particolare mi concentrerò sulla prima grande divisione dei linguaggi di programmazione: i linguaggi compilati e i linguaggi interpretati.
Senza entrare troppo nel dettaglio tecnico, dobbiamo per prima cosa spiegare come fa il vostro pc a sapere che 2+2 fa 4 e soprattutto come fa a capire perchè deve dirvelo.
Gli ingredienti sono due: un file binario (composto cioè da una serie di 0 e di 1) caricato da qualche parte (in genere la memoria) e un processore in grado di leggerlo.
Il processore è essenzialmente stupido: lui prende dal file i primi 32 (o 64) zeri-uni (bit) e li legge, capisce l’istruzione che deve fare, la esegue, e passa ai 32 bit successivi, oppure, se l’istruzione era un salto, ai 32bit piazzati in un punto specificato (per esempio) nei 32bit appena letti.
Le istruzioni codificate nei 32bit che il processore di volta in volta va a prendere sono le istruzioni assembly, sono strettamente collegate allo specifico processore e per questo variano da macchina a macchina.
Ma chi scrive il file binario? Ovviamente il programmatore. Ma come lo scrive? Ovviamente non mettendo in fila 0 e 1.
Potrebbe scriverlo in assembly. Ma il limite non è (come si pensa) la difficoltà bensì il fatto che esso varia da processore a processore e quindi un programma scritto interamente in assembly diventerebbe del tutto inutilizzabile su un processore diverso e andrebbe riscritto da capo.
E come si sa i programmatori sono pigri.
Per ovviare al problema nascono i linguaggi di programmazione compilati. I linguaggi di programmazione mettono a disposizione set di istruzioni (quasi sempre molto più leggibili e umane dell’assembly) che raggruppano una o più istruzioni macchina. Esempi di linguaggi compilati sono il C/C++, Fortran, Delphi e altri…
I linguaggi di programmazione prima di diventare eseguibili vanno per l’appunto compilati.
Un compilatore non è altro che un programma che traduce il testo scritto nel dato linguaggio in un listato assembly proprio della macchina su cui è in esecuzione. In questo modo lo stesso codice in un linguaggio di “alto” livello può venire compilato su macchine diverse e continuare a funzionare benissimo.
Il compilatore gcc per esempio permette (tramite il parametro -S se ben ricordo) di generare un file .s contenente il listato assembly invece di generare l’eseguibile vero e proprio.
In pratica l’unico programma che va riscritto quando si cambia tipo di processore è il compilatore.
Il listato assembly però non è ancora pronto per essere eseguito. Infatti esso va assemblato con un programma chiamato assemblatore. L’assemblatore non fa altro che codificare ogni istruzione assembly nella rispettiva serie di 0 e 1.
A questo macchine più moderne aggiungono il cosiddetto linker. Il Linker si incarica di attaccare in cima al file binario tutte quelle istruzioni che ne permettono una corretta esecuzione all’interno di un sistema operativo.
Questi tre elementi (compilatore-assemblatore-linker) sono le tre parti fondamentali per rendere eseguibile del codice scritto nel linguaggio di programmazione XYZ.
Col passare del tempo però è sorta la necessità di creare programmi che non sopportassero solo cambi di processore ma anche cambi di sistemi operativi. Nascono per questo i linguaggi di programmazione interpretati. Tali linguaggi non passano attraverso i tre elementi descritti in precedenza ma girano su una macchina virtuale in grado di leggere direttamenteil codice e di tradurlo al processore che può cosi eseguire. Esempi di linguaggi interpretati sono Java, Python, PHP, C# e molti altri.
Il processo di traduzione effettuato dalla macchina virtuale quando esegue un programma, essendo un passaggio in più, rende ovviamente meno efficiente l’esecuzione del programma.
In realtà quasi tutti i linguaggi interpretati moderni sono semi-compilati ovvero vengono compilati in uno pseudo-assembly (il bitcode di Java o il pyCode di Python) che viene eseguito sempre dalla macchina virtuale. Questo è uno dei molteplici espedienti utilizzati dai linguaggi interpretati per aumentare la velocità di esecuzione.
Come introduzione penso vada bene. Nella prossima lezione ci occuperemo invece di un ulteriore distinzione fra i linguaggi di programmazioni ovvero i linguaggi procedurali, object oriented e quelli logici.
Complimenti, bella serie (che spero proseguirai) 😉
Il java è compilato, non interpretato….