nomi ed oggetti denotabili int pippo; int pluto() { pippo=1; }

Post on 14-Jan-2016

52 views 0 download

description

nome: sequenza di caratteri usata per denotare un oggetto. 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 - PowerPoint PPT Presentation

Transcript of nomi ed oggetti denotabili int pippo; int pluto() { pippo=1; }

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 (alcuni linguaggi hanno associazioni implicite)

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

più puntatori a una stessa variabile

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 formali)

– 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

tempo di vita degli oggetti

tempo di vita delle associazioni

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