Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
vuoi
o PayPal
tutte le volte che vuoi
CREAZIONE DI UN PROGRAMMA COMPILATO:
● 1° GEN: E’ un codice macchina (binario) direttamente eseguibile; è il linguaggio
specifico della CPU (es. ...1101100101010101010…);
● 2° GEN: E’ un codice assembler, strettamente legato all’architettura (x86, MIPS,
PowerPC…). Ad es. LDA <ind >; ADD <ind > A; STA <ind >;
β γ α
● 3° GEN: E’ un codice sorgente ad alto livello (C, Basic, Java…); è indipendente
dall’architettura (es. ).
α = β + γ
I linguaggi hanno varie caratteristiche: necessità di traduzione, complessità del traduttore,
portabilità, leggibilità, efficienza, facilità di uso, probabilità di errore.
LINGUAGGI:
● Il linguaggio macchina (1° generazione):
- usa la codifica binaria di codice operativo e operandi;
- le istruzioni sono quelle direttamente eseguibili dalla CPU;
- i programmi sono utilizzabili solo su elaboratori equipaggiati con la CPU
corrispondente al linguaggio (non portabile);
- la programmazione è difficile;
- presenta una pessima leggibilità;
- presenta facilità di errori;
- presenta alta efficienza (dipende dal programmatore).
● I linguaggi assemblativi (2° generazione):
- le istruzioni sono quelle eseguibili dalla CPU;
- presenta la rappresentazione simbolica, tramite brevi mnemonici, di codici
operativi e operandi;
- presenta il riferimento alle variabile tramite il nome;
- c’è necessità di un traduttore in linguaggio macchina (assemblatore);
- c’è la possibilità di definire ed utilizzare macroistruzioni;
- i programmi sono utilizzabili solo su elaboratori equipaggiati con la CPU
corrispondente al linguaggio (non portabili);
- la programmazione è abbastanza difficile;
- presenta una scarsa leggibilità;
- presenta un’alta efficienza (dipende dal programmatore).
Come i compilatori traducono in codice eseguibile programmi scritti in un linguaggio
scritto ad alto livello, così gli assemblatori (o traduttori) traducono programmi scritti
nel linguaggio assembly (di basso livello) proprio del processore specifico. Mentre
per i linguaggi ad alto livello ad ogni statement corrispondono più istruzioni eseguibili
da parte del microprocessore, per quelli a basso livello si ha una corrispondenza uno
a uno fra statement del file sorgente ed istruzioni eseguibili. Gli assemblatori sono
tipicamente a due passate: nella prima viene effettuata l’analisi delle istruzioni e
creata la tavola dei simboli; la seconda passata produce il codice in linguaggio
macchina per il processore. I macroassemblatori ammettono macroistruzioni,
istruzioni definite dal programmatore a cui vengono fatte corrispondere più statement
del linguaggio assembly.
● I linguaggi di alto livello (3° generazione):
- le istruzioni esprimono operazioni significative nella logica del programmatore
non coincidenti con quelle eseguite dalla CPU;
- c’è la necessità di un traduttore (compilatore o interprete);
- la traduzione trasforma un’istruzione in una sequenza di istruzioni macchina;
- c’è un’efficienza ridotta rispetto ai linguaggi assemblativi (dipende dal
compilatore), ma possibilità di ottimizzazioni;
- c’è la portabilità di un programma su tutti gli elaboratori che dispongono di un
traduttore per il linguaggio;
- presenta l’introduzione di costrutti logici;
- presenta maggiore facilità d’uso;
- presenta una buona leggibilità.
I linguaggi ad alto livello vengono classificati in procedurali, funzionali, basati sulla logica,
orientati agli oggetti.
La scelta va fatta in funzione dei parametri seguenti: adattabilità al problema; facilità di
apprendimento; facilità d’uso; comprensibilità e modificabilità dei programmi; portabilità dei
programmi; efficienza; software di supporto; esistenza di librerie.
LINGUAGGI PROCEDURALI: I linguaggi procedurali sono basati sul modello
computazionale di Von Neumann, in cui un programma è visto come una sequenza di
istruzioni tendente a modificare il contenuto della memoria. Hanno varie caratteristiche:
- sono indipendenti dalla macchina;
- è possibile l’uso di nomi simbolici per i dati;
- la sintassi è vicina al linguaggio naturale;
- sono basati su istruzioni e assegnazioni;
- permettono astrazioni sui dati: c’è la possibilità di definire tipi di dati ed operazioni sui
tipi.
- permettono astrazioni sul controllo: l’ordine di esecuzione delle istruzioni è
determinato dalle strutture di controllo (FOR, WHILE, DO, …) o da moduli (funzioni
e procedure);
- permettono l’utilizzo dei sottoprogrammi: la definizione di nuove istruzioni e
operazioni;
- presenta una buona leggibilità.
I linguaggi procedurali si dividono in 3 categorie:
- i linguaggi d’uso generale (BASIC, C, C++, JAVA, FORTRAN, PASCAL, COBOL,
PL1, …);
- i linguaggi d’uso speciale (CHILL, SIMULA, APT, AED, SQL, …);
- i linguaggi per applicazioni Web (Perl, PHP).
AMBIENTE DI PROGRAMMAZIONE: L’ambiente di programmazione è l’insieme dei
programmi che consentono la scrittura, la verifica e l’esecuzione di nuovi programmi (fasi di
sviluppo).
Sviluppo di un programma: affinché un programma scritto in un qualsiasi linguaggio di
programmazione (ad es. C, FORTRAN, Java) sia comprensibile, e quindi eseguibile, da un
calcolatore, è necessaria un’azione di traduzione dal linguaggio originario al linguaggio
macchina. Questa operazione viene normalmente svolta da speciali programmi, detti
traduttori.
COMPILATORI: Un compilatore di un linguaggio L (Pascal, C, FORTRAN, …) per una
macchina X è un programma che: preso in ingresso un programma P scritto nel linguaggio
L, e che produce in uscita un programma P’ equivalente a P scritto nel linguaggio della
macchina X. Il vantaggio è la portabilità del sorgente.
Il compilatore traduce il programma sorgente in programma oggetto. La costruzione di un
compilatore per un particolare linguaggio di programmazione è abbastanza complessa. La
complessità dipende dal linguaggio sorgente. Un programma sorgente è una stringa di
simboli. Su di esso un compilatore esegue l’analisi del programma sorgente (front-end del
compilatore) e la sintesi del programma oggetto (back-end del compilatore).
Il processo di compilazione ha varie fasi: analisi lessicale, analisi sintattica, analisi
semantica, generazione del codice intermedio (oggetto), ottimizzazione e generazione
del codice eseguibile (programma target). Durante le varie fasi viene mantenuta una
tabella dei simboli e vengono segnalati errori ed anomalie.
● L’analisi lessicale è l’attività del compilatore tesa ad aggregare i caratteri di un
programma sorgente per riconoscere e classificare le stringhe appartenenti al
vocabolario (o lessico) del linguaggio di programmazione. Funziona in questo modo:
legge un carattere alla volta finché non identifica un simbolo di una categoria
sintattica del linguaggio di programmazione.
Il ruolo dell’analizzatore lessicale è di trasformare un programma sorgente (definito
da una sorgente di caratteri) in una sequenza di token. Esso riconosce parole
chiave, identificatori, costanti, separatori ed operatori, stringhe ecc.; inoltre elimina
spazi superflui, commenti e caratteri di controllo. Ad es. x = y - 3 viene suddiviso nei
token x, =, y, -, 3. Per le variabili e le funzioni viene creata una Tavola dei Simboli.
Una Tavola dei Simboli è costituita da una serie di righe ognuna delle quali contiene
una lista di valori di attributi associati con una particolare variabile/funzione. Gli
attributi dipendono dal linguaggio di programmazione che si compila. Si possono
considerare i seguenti attributi:
1. Nome della variabile/funzione;
2. Indirizzo nel codice oggetto;
3. Tipo;
4. Numero dei parametri di una funzione/dimensione della variabile.
ecc.
● L’analisi sintattica prende in ingresso i token generati dall’analizzatore lessicale e
attraverso una grammatica esegue il controllo sintattico del programma. Il risultato di
questa fase è un albero sintattico. E’ guidata dalla definizione formale del
Linguaggio di Programmazione (grammatica). Viene riconosciuta la struttura del
programma. Viene generata una forma intermedia (albero, matrice) corrispondente
alla struttura sintattica del programma.
Un albero sintattico (parse tree) fornisce una rappresentazione che mette in
evidenza la struttura del processo di derivazione di una frase da una grammatica. E’
la struttura usata per rappresentare un programma sorgente in un compilatore. Esso
facilita la traduzione del programma sorgente in un codice eseguibile.
Gli errori di sintassi sono relativamente semplici da trovare, in quanto infrangono
delle regole ben definite per la scrittura del codice; sono segnalati in modo
automatico dagli strumenti usati per lo sviluppo di un programma (ad es. scrivere wile
invece di while, dimenticare di chiudere una parentesi, dimenticare il ; a fine
istruzione…).
● Oltre che essere lessicalmente e sintatticamente corretto, il programma deve esserlo
anche semanticamente. L’analisi semantica effettua dei controlli: verifica la
compatibilità di tipo di espressioni legate da operatori e la correttezza delle
dichiarazioni degli identificatori; queste verifiche vengono effettuate sulla forma
intermedia generata in precedenza (albero, matrice). Questi controlli possono essere
più o meno approfonditi a seconda delle caratteristiche del linguaggio. Una vera
analisi semantica permetterebbe di individuare il significato del programma, cioè il
suo scopo.
Gli errori semantici vengono tipicamente segnalati in modo automatico, una volta
che si è verificato che il programma è sintatticamente corretto. (ad es. richiamare una
funzione che non esiste, confrontare un numero intero con una stringa, assegnare un
valore ad una costante [3 = x] o assegnare un valore ad un’espressione [x + y = 3]).
● Dalla forma intermedia prodotta precedentemente (albero) si passa alla generazione
di un codice di basso livello, ma ancora indipendente dalla CPU per la quale si
vuole che il codice macchina sia generato. Questo codice può essere ottimizzato con
facilità e quindi può essere tradotto nelle istruzioni della particolare CPU. I codici
oggetto sono parti di programma contenenti sia codice macchina che informazioni
per il linker. Quest’informazione è costituita principalmente nella forma di definizioni
di simboli, che sono di due tipi:
- i simboli definiti o esportati sono simboli che rappresentano quelle funzioni
o quelle variabili