Anteprima
Vedrai una selezione di 7 pagine su 26
Appunti di Linguaggi di Programmazione Pag. 1 Appunti di Linguaggi di Programmazione Pag. 2
Anteprima di 7 pagg. su 26.
Scarica il documento per vederlo tutto.
Appunti di Linguaggi di Programmazione Pag. 6
Anteprima di 7 pagg. su 26.
Scarica il documento per vederlo tutto.
Appunti di Linguaggi di Programmazione Pag. 11
Anteprima di 7 pagg. su 26.
Scarica il documento per vederlo tutto.
Appunti di Linguaggi di Programmazione Pag. 16
Anteprima di 7 pagg. su 26.
Scarica il documento per vederlo tutto.
Appunti di Linguaggi di Programmazione Pag. 21
Anteprima di 7 pagg. su 26.
Scarica il documento per vederlo tutto.
Appunti di Linguaggi di Programmazione Pag. 26
1 su 26
D/illustrazione/soddisfatti o rimborsati
Disdici quando
vuoi
Acquista con carta
o PayPal
Scarica i documenti
tutte le volte che vuoi
Estratto del documento

Nel momento in cui, durante l'esecuzione di un programma, si entra in un nuovo blocco vengono

create le associazioni fra i nomi dichiarati localmente al blocco e disattivate le associazioni per quei

nomi già esistenti all'esterno del blocco che sia ridefiniti al suo interno. Nel caso dell'uscita dal

nuovo blocco, invece, si verifica l'opposto le associazioni precedentemente create vengono distrutte

e quelle disattivate vengono riattivate. Possiamo identificare delle operazioni sui nome e

sull'ambiente, quali riferimento di oggetto denotato mediante il suo nome, creazione, disattivazione,

riattivazione e distruzione e di un'associazione fra il nome e l'oggetto denotato. Per quanto riguarda

gli oggetti denotabili sono possibili le operazioni di creazione, accesso, modifica e distruzione.

In molti casi le operazioni di creazione fra nome e oggetto avvengono allo stesso tempo, ma non

vale sempre. In generale non è detto che il tempo di vita di un oggetto denotabile, ossia il tempo che

intercorre fra la sua creazione e la sua distruzione, coincida con il tempo di vita dell'associazione fra

nome e oggetto. Infatti un oggetto può avere un tempo di vita maggiore di quello della sua

associazione con un nome. Può accadere anche che il tempo di vita di un 'associazione sia superiore

a quello dell'oggetto, cioè può avvenire che un nome permetta di accedere ad un oggetto che non

esiste più. Questo può accadere, per esempio, se si passa per riferimento un oggetto creato e quindi

si dealloca la memoria per tale oggetto prima che la procedura termini.

Regole di scope

In un linguaggio con scope statico, un qualsiasi ambiente esistente dipende solo dalla strutture

sintattica del programma. L a regola dello scope statico è definita dai seguenti tre punti:

1. Le dichiarazioni locali di un blocco definiscono l'ambiente locale di quel blocco e non

quelle presenti nei suoi eventuali blocchi annidati.

2. Se si usa un nome all'interno di un blocco, l'associazione valida per tale nome è quella

presente nell'ambiente locale del blocco, se esiste. Se non esiste, si considerano le

associazione esistenti nell'ambiente locale del blocco immediatamente esterno che contiene

il blocco.

3. Un blocco può avere un nome, nel qual caso tale nome fa parte dell'ambiente locale del

blocco immediatamente esterno che contiene il blocco a cui abbiamo dato un nome.

Lo scope statico ha due importanti conseguenze positive; innanzitutto in programmatore ha una

migliore comprensione del programma osservandone la struttura testuale, inoltre il compilatore può

collegare ogni occorrenza di un nome alla sua dichiarazione corretta, questo fa si che siano possibili

a tempo di compilazione un maggior numero di controlli di correttezza. D'altro canto risulta meno

efficiente in fase di esecuzione.

Lo scope dinamico è stato introdotto principalmente per semplificare la gestione a run-time

dell'ambiente. Molti linguaggi lo usano e determinano le associazioni fra nomi e oggetti denotati

seguendo a ritroso l'esecuzione del programma. Tali linguaggi per risolvere i nomi non locali usano

la sola struttura a pila impiegata per la gestione a run-time dei blocchi. Secondo la regola dello

scope dinamico, l'associazione valida per un nome X, in un qualsiasi punto P di un programma, è la

più recente, in senso temporale, associazione creata per X che sia ancora attiva quando il flusso di

esecuzione arriva a P. Questo metodo ha il vantaggio di avere un'ottima flessibilità, ma ha una

minore leggibilità e una complicata gestione a run-time.

Esistono alcuni problemi di scope; le differenza maggiori fra le regole di scope statico sono relative

a dove possono essere introdotte le dichiarazioni e a quale sia l'esatta visibilità delle variabili locali.

Nel caso del Pascal le dichiarazioni possono comparire solo all'inizio del blocco e prima di essere

usati, inoltro lo scope di un nome si estende dall'inizio alla fine del blocco nel quale appare la

dichiarazione. Nel caso del C e di ADA lo scope della dichiarazione viene limitato alla porzione di

blocco compresa fra il punto in cui la dichiarazione compare e la fine del blocco stesso; anche in

questi linguaggi i nomi devono essere dichiarati prima di essere usati. Però la dichiarazione prima

dell'uso in alcuni casi è particolarmente gravosa: impedisce infatti la definizione di tipi ricorsivi. In

JAVA è consentito che una dichiarazione possa apparire in un punto qualsiasi di un blocco. Se la

dichiarazione corrisponde ad una variabile, lo scope del nome dichiarato si estende dal punto della

dichiarazione fino alla fine del blocco; se la dichiarazione invece si riferisce al membro di una

classe, essa è visibile in tutta la classe nella quale compare.

5 – Gestione della Memoria

Tecniche di gestione della memoria

La gestione della memoria costituisce una delle funzionalità dell'interprete, quali la gestione

dell'allocazione di memoria per i programmi e i dati: come disporli in memoria, quanto tempo ci

devono rimanere e quali strutture ausiliarie siano necessarie. Nel caso di una macchina stratta di

baso livello tipo la macchina hardware, la gestione della memoria è molto semplice e può essere

interamente statica. Nel caso di un linguaggio di alto livello l'allocazione statica della memoria non

è più sufficiente, per cui dobbiamo usare una gestione dinamica della memoria. Questa gestione può

essere realizzata con una pila, ma ci sono casi dove la pila non è sufficiente; in questo caso si usa

una struttura detta heap.

Gestione statica della memoria

La memoria gestita staticamente è quella allocata dal compilatore prima dell'esecuzione. Gli oggetti

per i quali la memoria è allocata staticamente risiedono in una zona fissa di memoria per tutta la

durata dell'esecuzione. Tipici elementi per i quali è possibile allocare staticamente la memoria sono

le variabili locali, le istruzioni del codice oggetto prodotte dal compilatore, le costanti e le varie

tabelle prodotte dal compilatore necessarie per il supporto a run-time del linguaggio. Nel caso in cui

il linguaggio non sopporti la ricorsione, è possibile gestire staticamente anche a memoria per le

rimanenti componenti del linguaggio: sostanzialmente si tratta di associare, staticamente, ad ogni

procedura una zona di memoria nella quale memorizzare le informazioni della procedura stessa.

Gestione dinamica mediante pila

La maggior parte dei linguaggi di programmazione moderni permette una strutturazione a blocchi

dei programmi. I blocchi vengono aperti e chiusi usando la politica LIFO: quando si entra in un

blocco A e poi in un blocco B, prima di uscire da A si deve uscire da B. Lo spazio di memoria

allocato sulla pila è detto record di attivazione (RdA). Il record di attivazione è associato ad una

specifica attivazione di procedura e non ad una sua dichiarazione. La pila sulla quale sono

memorizzati i record di attivazione è detta pila a run-time.

La struttura di un record di attivazione per un blocco in-line è composta da vari settori che

contengono le seguenti informazioni:

Risultati intermedi: nel caso in cui si debbano effettuare dei calcoli può essere necessario

– memorizzare alcuni risultati intermedi.

Variabili locali: sono dichiarate all'interno di un blocco, devono avere a disposizione uno

– spazio di memoria la cui dimensione dipenderà dal numero e dal tipo delle variabili; in

alcuno casi vi possono essere dichiarazioni che dipendono da valori noti solo al momento

dell'esecuzione, come ad esempio gli array dinamici. In questi casi il record di attivazione

prevede anche una parte di dimensione variabile che sarà definita al momento

dell'esecuzione.

Puntatore di catena dinamica: questo campo serve per memorizzare il puntatore al

– precedente record di attivazione sulla pila, essendo gli Rda con dimensioni diverse; l'insieme

dei collegamenti realizzati da questi puntatori è detto catena dinamica.

La struttura di un record di attivazione per le procedure e le funzioni è formato dai seguenti campi:

Risultati intermedi, variabili locali e puntatore di catena dinamica.

– Puntatore di catena statica: serve per gestire le informazioni necessarie a realizzare le regole

– di scope statico.

Indirizzo di ritorno: contiene l'indirizzo della prima istruzione da eseguire dopo che la

– chiamata alla procedura ha terminato l'esecuzione.

Indirizzo del risultato: serve solo per le funzioni e restituisce l'indirizzo della memoria dove

– viene depositato il risultato.

Parametri: contiene i valori dei parametri attuali usati nella chiamata della procedura.

Per gestire la pila viene usato un puntatore esterno alla pila che indica l'ultimo RdA inserivo nella

pila stessa, chiamato puntatore ad record di attivazione. Un altro puntatore, chiamato puntatore alla

pila, indica la prima posizione di memoria libera nella pila. I record di attivazione vengono inseriti e

rimossi dalla pila a tempo di esecuzione: quando si entra in un blocco, o si chiama una procedura, il

relativo RdA verrà inserito nella pila, per poi essere eliminato quando si esce dal blocco o quando

termina l'esecuzione della procedura. La gestione a run-time della pila di sistema è realizzata da

alcuni frammenti di codice che il compilatore inserisce prima e dopo la chiamata di una procedura o

prima dell'inizio e dopo la fine di un blocco. La gestione della pila è fatta sia dal programma o

procedura chiamante che dal programma o procedura chiamato. Per questo scopo nel chiamante

viene aggiunta una parte di codice detta sequenza di chiamata che è eseguita in parte

immediatamente prima della chiamata di procedura, e in parte immediatamente dopo la

terminazione della procedura. Nel chiamato invece viene aggiunto un prologo, da eseguirsi subito

dopo la chiamata, ed un epilogo, da eseguirsi al termine dell'esecuzione della procedura. Al

momento della chiamata di procedura la sequenza di chiamata ed il prologo si devo occupare delle

seguenti attività:

Modifica del valore del contatore programma

– Allocazione dello spazio sulla pila

– Modifica del puntatore RdA

– Passaggio dei parametri

– Salvataggio dei registri

– Esecuzione del codice per l'inizializzazione

Al momento del ritorno del controllo al programma chiamante, quando la procedura chiamata

termina la sua esecuzione, l'epilogo e la sequenza di chiamata devono invece gestire le seguenti

operazioni:

Ripristino del valore del contatore programma

– Restituzione dei valori

– Ripristino dei registri

– Esecuzione del codice per la finalizzazione

– Deallocazione dello spazio sulla pila

Gestione dinamica mediante heap

Nel caso in cu

Dettagli
Publisher
A.A. 2012-2013
26 pagine
SSD Scienze matematiche e informatiche INF/01 Informatica

I contenuti di questa pagina costituiscono rielaborazioni personali del Publisher Gabriele515 di informazioni apprese con la frequenza delle lezioni di Linguaggi di programmazione e studio autonomo di eventuali libri di riferimento in preparazione dell'esame finale o della tesi. Non devono intendersi come materiale ufficiale dell'università Università degli Studi di Bari o del prof Fanizzi Nicola.