Aliasing (programmazione)In programmazione, il termine aliasing indica la situazione in cui una stessa posizione di memoria è associata a nomi simbolici diversi all'interno di un programma. SignificatoTutti i simboli coinvolti (in inglese aliased names) fanno da "ponte" verso una stessa zona di memoria: di conseguenza, le informazioni scritte servendosi di uno di essi sono "visibili" tramite tutti gli altri, perché i dati interessati dall'operazione di scrittura non si trovano direttamente nel simbolo, ma in una zona di memoria "comune". EsempiNel seguito è presentato un elenco (non esaustivo) di meccanismi che permettono di avere aliasing nei linguaggi di programmazione che li supportano. Puntatori![]() i e il puntatore p .Un modo semplice di realizzare un alias in C++ è servirsi di un puntatore. Ad esempio, int i = 0;
int* pi = &i;
La zona di memoria occupata dalla variabile * pi = 5;
cout << i;
in quanto l'istruzione
Lo standard ISO per il linguaggio di programmazione C (inclusa la nuova versione C99, vedere sezione 6.5, paragrafo 7) proibisce, con alcune eccezioni, che puntatori di tipi indipendenti tra loro referenzino la stessa posizione di memoria. Questa regola è nota come strict aliasing. ArrayAlcuni linguaggi di programmazione, come il C e il C++, non impongono alcun controllo sugli indici utilizzati per accedere alle celle degli array (il cosiddetto array bounds checking). In altre parole, è possibile che un programma acceda a celle dell'array che non gli appartengono. Sotto particolari condizioni, questo meccanismo permette di accedere ad una variabile tramite l'array. ![]() i è stata allocata nella cella di memoria immediatamente successiva all'ultima cella che è stata riservata all'array a .Ad esempio, se Questo è possibile perché gli array sono realizzati in realtà come blocchi di posizioni di memoria contigue, così che il programma memorizza internamente un puntatore Risulta evidente che questo meccanismo è reso possibile dalla particolare implementazione del compilatore in uso: per esempio, implementazioni differenti potrebbero porre dello spazio tra gli array e le variabili allocate sullo stack delle invocazioni a funzione, così da allineare le variabili a posizioni di memoria che siano multiple della word specifica dell'architettura del sistema in uso. Questo è possibile perché il C non impone una regola generale che stabilisca come debbano essere disposti i dati allocati in memoria.[1] UnioniLe unioni sovrappongono variabili differenti in una stessa posizioni di memoria. L'uso delle unioni per accedere ad una stessa posizione di memoria tramite variabili differenti è tuttavia sconsigliato. Programmazione orientata agli oggettiNei linguaggi di programmazione orientati agli oggetti, il fenomeno dell'aliasing si verifica quando più variabili contengono copie di uno stesso riferimento ad un certo oggetto. Si parla di riferimenti e non necessariamente di puntatori perché questo meccanismo si trova anche in quei linguaggi che non fanno uso di puntatori, come il Java. C++In C++ gli oggetti sono "contenuti" direttamente nelle variabili, nel senso che in ogni variabile sono memorizzati proprio i dati che compongono l'oggetto e non già un puntatore ad esso. La copia di una variabile di tipo oggetto in un'altra sovrascrive i dati contenuti nella prima con i dati contenuti nella seconda, tuttavia mantiene distinte le rispettive zone di memoria; di conseguenza, le successive operazioni di scrittura effettuate su una variabile oggetto interessano solo quella copia specifica e non influenzano né l'originale, né le altre copie. Per ottenere l'aliasing, bisogna definire esplicitamente un puntatore all'area di memoria occupata dall'oggetto. Essa può essere associata ad una variabile (nel qual caso il puntatore deve referenziare la variabile) o essere stata allocata dinamicamente, cioè tramite l'operatore In alternativa, si può progettare appositamente la classe interessata, in modo che le diverse copie di uno stesso oggetto utilizzino internamente dei puntatori ad una zona di memoria comune (allocata tramite JavaIn Java vige la seguente regola: non si accede mai direttamente ad un oggetto, ma sempre e solo tramite un reference che punta a esso. A livello concettuale, i reference possono essere considerati come puntatori utilizzabili con determinate restrizioni: ad esempio, non supportano le operazioni aritmetiche, non possono essere usati per deallocare esplicitamente la memoria, ed è possibile referenziare solo oggetti (non esistono "reference a variabili"). Una variabile non contiene direttamente i dati dell'oggetto, ma un reference che punta all'oggetto stesso. Di conseguenza, copiare una variabile significa in realtà copiare il reference, e ovviamente questo significa creare due alias che puntano all'oggetto. In effetti, l'operatore Nel codice seguente dapprima si crea una lista, la si modifica tramite una variabile ArrayList<String> stringhe = new ArrayList<String>();
ArrayList<String> listaDiStringhe = stringhe;
// Aggiungo una stringa alla lista tramite il reference contenuto nella variabile stringhe:
stringhe.add("abcde");
// Quindi la riga seguente scriverà "abcde" su schermo:
System.out.println(stringhe.get(0));
// stringhe e listaDiStringhe si riferiscono allo stesso oggetto, di conseguenza
// anche la riga seguente scriverà "abcde" su schermo:
System.out.println(listaDiStringhe.get(0));
VantaggiIn alcuni casi l'aliasing viene usato intenzionalmente. Ad esempio, esso è d'uso comune in Fortran; in altri linguaggi può essere usato apposta per trarne determinati benefici. Il Perl definisce per alcuni costrutti, come il for-each, un comportamento che sfrutta le caratteristiche dell'aliasing, il che permette di modificare facilmente determinate strutture dati con codice più sintetico e intuitivo. Ad esempio: my @array = (1, 2, 3);
foreach my $element (@array) {
# $element fa da ''alias'' per ciascuno degli
# elementi di @array, uno per ogni ciclo,
# quindi incrementare $element nell'i-esimo ciclo
# significa modificare l'i-esimo elemento
# dell'array.
$element++;
}
print "@array \n";
Questo codice stampa come risultato la riga SvantaggiIn determinati casi l'aliasing delle variabili può portare dei problemi in fase di esecuzione del programma. Seguono alcuni esempi comuni. Effetti collateraliL'aliasing dei parametri passati a una subroutine permette a quest'ultima di modificarne il valore, generando così un effetto collaterale (in genere non desiderato). Aliasing e ottimizzazioneL'aliasing spesso ostacola o rende più complesso il compito di ottimizzare il codice eseguibile affidato a compilatori o programmi appositi. Inlining dei valoriPer esempio, si supponga di avere nel programma una variabile Per questo motivo, il compilatore deve svolgere controlli aggiuntivi e raccogliere informazioni sui puntatori che sono definiti nel programma, e chiedersi: " Instruction reorderingUn'altra tecnica per ottimizzare i programmi è cambiare l'ordine delle singole istruzioni che sono eseguite in una subroutine (facendo in modo che i cambiamenti non siano visibili dall'esterno della stessa: il codice ottimizzato produce gli stessi risultati ma con una differente sequenza di istruzioni). Se il compilatore stabilisce che XOR swapIl noto algoritmo chiamato in inglese XOR swap scambia tra loro due variabili numeriche senza l'ausilio di una terza variabile temporanea. L'algoritmo viene applicato su due ingressi numerici
Questo algoritmo funziona solo se i valori void xorSwap (int *x, int *y) {
if (*x != *y) {
*x ^= *y;
*y ^= *x;
*x ^= *y;
}
}
Nel codice presentato si confrontano i valori dei due ingressi, invece che i rispettivi puntatori, in quanto lo scambio di due valori uguali è evidentemente superfluo. Una versione alternativa, che fa uso del passaggio dei parametri per riferimento invece che per puntatore: void xorSwap (int& x, int& y) {
if (x != y) {
x ^= y;
y ^= x;
x ^= y;
}
}
Note
Collegamenti esterni
|
Portal di Ensiklopedia Dunia