Nomi ed oggetti denotabili int pippo; int pluto() { pippo=1; } –distinguere il nome e loggetto...
-
Upload
lorenza-fabris -
Category
Documents
-
view
218 -
download
0
Transcript of Nomi ed oggetti denotabili int pippo; int pluto() { pippo=1; } –distinguere il nome e loggetto...
nomi ed oggetti denotabili
int pippo;
int pluto() { pippo=1; }
– distinguere il nome e l’oggetto denotato – uno stesso oggetto può avere più nomi (aliasing)– uno stesso nome può denotare oggetti diversi in momenti
diversi– l’uso dei nomi realizza la più semplice astrazione sui dati
(l’ambiente è la parte dell’implementazione responsabile dell’associazione nome-oggetto denotato)
– l’uso dei nomi realizza la più semplice astrazione sul controllo
nome: sequenza di caratteri usata per
denotare un oggetto
oggetti denotabili: oggetti a cui posso assegnare un nome – oggetti i cui nomi sono definiti dall’utente (variabili, parametri, procedure tipi definiti, costanti simboliche, …)– oggetti i cui nomi sono definiti dal linguaggio di programmazione (tipi primitivi, operazioni primitive, …)
il binding (legame) nome-oggetto avviene in differenti fasi:
– progettazione del linguaggio– scrittura del programma– compile-time– run-time
ambiente: insieme delle associazioni nome-oggetto esistenti a run-time in uno specifico punto del programma ed in uno specifico momento
dichiarazione:costrutto del linguaggio che permette di introdurre un’associazione nell’ambiente
int x;
int f() { return 0; }
typedef T = int;
stesso nome denota oggetti diversi
{ int pippo;
pippo = 2;
{ char pippo;
pippo = a; } }
stesso oggetto denotato da più nomi in ambienti diversi
una variabile passata per riferimento è accessibile:– con il suo nome nel programma chiamante– con il nome del parametro formale nella procedura
variabile intera
variabile carattere
stesso oggetto denotato da più nomi, stesso ambiente
int *X, *Y;
X = (int *) malloc (sizeof (int));
*X = 5;
Y = X;
*Y = 10;
write(*X);
stesso nome, nello stesso ambiente, denota oggetti diversi a seconda del flusso di esecuzione del programma
– procedura ricorsiva che dichiara un nome locale– scope dinamico
aliasing
il risultato è 10, perché X e Y puntano alla stessa locazione
blocco:
regione testuale di un programma, individuata da un segnale di inizio e uno di fine, che può contenere dichiarazioni locali a quella regione
– blocco associato a una procedura– blocco in-line (o anonimo)
{ int pippo; pippo = 2;
{ char pippo; pippo = a; } }
i cambiamenti dell’ambiente avvengono all’entrata e all’uscita dai blocchi (anche annidati)
blocco in-line apri blocco A;apri blocco B;
chiudi blocco A;chiudi blocco B;
mai permesso
regola di visibilità canonica:
una dichiarazione locale in un blocco è visibile:– in quel blocco– in tutti i blocchi annidati– salvo ri-dichiarazioni dello stesso nome (mascheramento)
ambiente associato a un blocco:
– ambiente locale (nomi dichiarati localmente al blocco, eventualmente parametri locali)
– ambiente non locale (nomi dichiarati fuori dal blocco, ma comunque visibili)
– ambiente globale (nomi creati all’inizio del programma, usabili in tutti i blocchi)
A: { int a =1;
B: { int b=2; int c=2;
C: { int c=3; int d;
d=a+b+c;
write(d);
}
D: { int e;
e=a+b+c;
write(e);
}}}
associazione nell’ambiente globale
ambiente locale di B + ambiente globale
d=6;l’ambiente locale di C maschera quello di B
e=5
ambiente locale di C + ambiente non locale +
ambiente globale
nell’ambiente non locale di D non compare d
operazioni sull’ambiente
entrando in un blocco:– creazione delle associazioni fra i nomi locali al blocco e gli oggetti denotati– disattivazione delle associazioni per i nomi ridefiniti
uscendo dal blocco:– distruzione delle associazioni fra i nomi locali al blocco e gli oggetti denotati– riattivazione delle associazioni per i nomi che erano stati ridefiniti
operazioni sui nomi e sull’ambiente
– naming: creazione di associazione fra nome e oggetto denotato (dichiarazione locale al blocco o parametro)
– referencing: riferimento di un oggetto denotato mediante il suo nome (uso del nome per accedere all’oggetto denotato)
– disattivazione di associazione fra nome e oggetto denotato (la nuova associazione per un nome maschera la vecchia associazione, che rimane disattivata fino all’uscita dal blocco)
– riattivazione di associazione fra nome e oggetto denotato (una vecchia associazione che era mascherata è riattivata all’uscita da un blocco)
– unnaming: distruzione di associazione fra nome e oggetto denotato (all’uscita di un blocco)
operazioni su oggetti denotabili
– creazione: allocazione della memoria e inizializzazione
– accesso e modifica: tramite il nome
– distruzione: deallocazione della memoria
regole di scope
A: { int x=0;
void pippo(){ x=1;}
B: { int x;
pippo();
}
write(x);
}
quale valore di x è stampato?
regola di visibilità canonica:
una dichiarazione locale in un blocco è visibile:– in quel blocco– in tutti i blocchi annidati– salvo ri-dichiarazioni dello stesso nome (mascheramento)
MA
la regola di visibilità (cioè l’annidamento) è basata:– sul testo del programma (scope statico) ?– sul flusso di esecuzione (scope dinamico)?
scope statico o scope annidato più vicino
l’ambiente in qualsiasi punto e in qualsiasi momento dipende SOLO dalla struttura sintattica del programma
(basic, algol, ada, pascal, java)
cioè1. le dichiarazioni locali in un blocco includono solo quelle
presenti nel blocco, e non quelle dei blocchi annidati;
2. se si usa un nome in un blocco, l’associazione valida è quella locale al blocco; se non esiste, si prende la prima associazione valida a partire dal blocco immediatamente esterno, dal più vicino al più lontano; se non esiste, si considera l’ambiente predefinito del linguaggio; se non esiste, errore;
3. se il blocco ha un nome, allora il nome fa parte dell’ambiente locale del blocco immediatamente esterno (caso procedure).
che succede al compilatore con questa regola?
esempio con scope statico
{
int x=0;
void pippo(int n) {x=n+1;}
pippo(3);
write(x);
{int x=0;
pippo(3);
write(x);
}
write(x);
}
stampa 4
stampa 0
stampa 4
lo scope statico permette di determinare tutti gli ambienti di un programma leggendone il testo
– migliore comprensione del programma– controlli di correttezza a compile-time– ottimizzazione del codice a compile-time– problema del tipaggio statico e della sicurezza dei tipi
– gestione a run-time complicata (gli ambienti non locali evolvono diversamente dal flusso di attivazione e disattivazione dei blocchi)
{
const x=0;
void pippo() {write(x);}
void pluto() {const x=1;
pippo();}
pluto();
}
con lo scope statico la sequenza di blocchi da considerare per risolvere i riferimenti a nomi non locali è diversa da quella dei blocchi aperti e chiusi durante l’esecuzione (gestibile LIFO)
seguendo il flusso del programma, con scope statico, è stampato 0, mentre ci aspettavamo 1, essendo pluto l’ultimo blocco attivo
scope dinamico
l’associazione valida per un nome X, in un punto P di un programma, è la più recente associazione creata (in senso temporale) per X che sia ancora attiva quando il flusso di esecuzione arriva a P
scope statico e scope dinamico sono uguali per la determinazione dell’ambiente locale e di quello globale
– gestione a run-time semplice ma poco efficiente(LIFO sulle attivazioni dei blocchi)– flessibilità nei programmi– difficile comprensione delle chiamate delle procedure
{
const x=0;
void pippo() {write(x);}
void pluto() {const x=1;
{const x=2;
}
pippo();}
pluto();
}
stampa 1 con scope dinamico, perché considera la più recente
associazione ancora attiva
problemi di scope statico– dove introdurre le dichiarazioni– qual è la visibilità delle variabili– definizioni ricorsive
beginconst pippo = valore;const valore = 0;
end
valore usato prima della definizione
beginconst valore = 1;procedure plutobegin const pippo = valore; const valore = 0;end
end
valore maschera la definizione esterna
nella procedura
errore: elemento è usato prima di essere definito; in
Pascal si può fare, ma solo con i puntatori
in ADA si usano dichiarazioni incomplete
type lista = ^elemento;type elemento = record
informazione: intero;successivo: listaend
type elemento;type lista = access elemento;type elemento is record
informazione: intero;successivo: listaend
in Pascal si usano definizioni incomplete per le funzioni
mutuamente ricorsive; in C si può fare liberamente
procedure pippo (A: integer); forward;
procedure pluto (B: integer)begin pippo(3);end
procedure pippo;begin pluto(4)end