Introduzione ai Puntatori e Indirizzi di Memoria
I tipi di riferimento memorizzano gli indirizzi di memoria degli oggetti. Questi tipi permettono una programmazione efficiente e molti pattern di design eleganti si basano su di essi. In questo capitolo esploreremo i due tipi principali: puntatori e reference, per poi discutere argomenti correlati come const, auto e il puntatore this.
I puntatori sono il meccanismo fondamentale usato per riferirsi agli indirizzi di memoria. Codificano due informazioni essenziali:
- L’indirizzo in memoria dell’oggetto.
- Il tipo dell’oggetto.
“Everyone knows that debugging is twice as hard as writing a program in the first place. So if you’re as clever as you can be when you write it, how will you ever debug it?” — Brian Kernighan
I puntatori sono oggetti di bassissimo livello. Sebbene siano centrali nel C, il C++ offre costrutti di livello più alto e più sicuri, ma i puntatori rimangono un concetto basilare che incontrerai sicuramente, specie nella programmazione di sistema.
Indirizzare le Variabili
Section titled “Indirizzare le Variabili”Puoi ottenere l’indirizzo di una variabile anteponendo l’operatore address-of (indirizzo di), ovvero &. Questo operatore ti permette di inizializzare un puntatore in modo che “punti” alla variabile corrispondente.
Si dichiara il tipo di un puntatore aggiungendo un asterisco (*) al tipo dell’oggetto puntato:
int* my_ptr; // my_ptr è un puntatore a intVediamo un esempio di come ottenere e stampare un indirizzo di memoria:
Se esegui questo codice, l’indirizzo stampato cambierà ad ogni esecuzione. Perché?
Address Space Layout Randomization (ASLR)
Section titled “Address Space Layout Randomization (ASLR)”Questa variazione è dovuta a una funzionalità di sicurezza dei sistemi operativi moderni chiamata Address Space Layout Randomization (ASLR). L’ASLR “mescola” le posizioni in memoria in cui vengono caricati i dati e il codice per ostacolare l’esecuzione di codice malevolo (exploit come la tecnica Return-Oriented Programming). Non potendo prevedere a priori a che indirizzo risiederà una certa funzione, gli attaccanti faticano molto di più a dirottare l’esecuzione.
Inoltre, un puntatore assume la dimensione del registro generale della CPU: su un’architettura a 32 bit (x86) un puntatore occuperà 4 byte, mentre su una a 64 bit (x64) occuperà 8 byte.
Dereferenziare i Puntatori
Section titled “Dereferenziare i Puntatori”L’operatore di dereferenziazione (*) è un operatore unario che accede all’oggetto a cui il puntatore si riferisce. È l’operazione inversa rispetto all’operatore &. Dato un indirizzo, l’operatore * ti fa ottenere l’oggetto residente a quell’indirizzo.
Attenzione alla sintassi: L’asterisco in C++ ha molteplici significati a seconda del contesto:
- Nella dichiarazione definisce un tipo puntatore:
int* ptr; - Come operatore unario, dereferenzia un puntatore:
*ptr = 42; - Come operatore binario, esegue una moltiplicazione:
int a = 5 * 3;
Come puoi vedere, modificare *gettysburg_address significa modificare direttamente il contenuto di gettysburg.