Non lo puoi fare/non si puo' fare.
E' al di fuori del tuo controllo!
E' come chiedere: ma perche' il sasso su cui sono inciampato, stava proprio li?
Comunque, una risposta esiste, ed e' la seguente: ha a che fare con gli algoritmi di gestione/allocazione della memoria del processo.
Ci sono due livelli di allocazione:
1) quando il programma chiede un blocco di memoria al
gestore di memoria (cioe' agli algoritmi su cui la
malloc e' implementata), questa viene allocata da quella assegnata al processo.
2) se non c'e' ne abbastanza, il processo chiede al S.O un nuovo blocco di memoria abbastanza grande.
Questo perche' non sarebbe efficiente, a livello di sistema operativo, gestire tutte le microscopice allocazioni fatte da tutti i programmi. Quindi il SO assegna blocchi al processo, ed il processo suddivide i blocchi in blocchettini piu' piccoli che poi usa.
Questo e' anche comodo perche' cosi quando il processo termina (o schianta), il SO sa quali sono i blocchi di memoria assegnati e li ricupera.
Come viene fatta tutta questa gestione?
E' abbastanza complicato: tieni presente che il gestore della memoria NON DEVE solo ALLOCARE la memoria richiesta, ma anche tenere traccia di quella rilasciata.
E lo deve fare in un contesto multithreading, ed essendo la memoria unica, deve assicurarsi di non fare pasticci.
Ma come fa il gestore della memoria, sapere quanta memoria e' stata allocata e che cosa farne quando viene rilasciata?
Usa delle informazioni memorizzate in TESTA (accessibili usando indici NEGATIVI) ed in CODA (cioe' andando a leggere OLTRE lo spazio che hai allocato) al tuo blocco di memoria appena allocato:
c'e' l'informazione della dimensione del blocco, di quale e' il blocco successivo, quello precedente, marcatori di validita' del blocco, ecc.
Quando tu vai a leggere i dati memorizzati AL DI FUORI del blocco che hai allocato, stai semplicemente andando a leggere le informazioni di servizio gestite dal gestore della memoria.
Quali sono queste informazioni?
Non sono standard, dipendono TOTALMENTE da come il gestore di memoria e' stato implementato.
Per poterlo scoprire devi:
1) ricuperare i sorgenti della malloc/calloc/free del compilatore C che stai usando (esistono sicuramente, comprenderli e' un'altro paio di maniche)
2) usare un nuovo gestore di memoria, di cui hai i sorgenti (ne esistono molti: jmalloc tcmalloc, dmalloc, ecc.)
3) implementare un tuo gestore di memoria : cosa che si fa abbastanza spesso quando le performance di quello standard lasciano a desiderare (si, ci sono casi in cui conviene farlo ).
Ma la storia non e' finita qui'.
Per il C++ le cose si complicano: l'implementazione della new e della delete e' basata sulla malloc/free, ma il C++ aggiunge ulteriori informazioni relative alla dimensione del vettore (che serve in fase di deallocazione di classi con distruttore, fatta con a delete[]) e chissa che altro.
Finito? Non ancora: cosa succede se tu
1) allochi 500 byte, ci scrivi qualcosa dentro
2) li rilasci
3) allochi 1000 byte?
Molto PROBABILMENTE quello che succede e' che nei primi 500 byte del nuovo blocco (che POTREBBE avere lo stesso indirizzo del precedente) trovi esattamente quello che hai scritto al punto 1).
Nota: PROBABILMENTE/POTREBBE, ma non e' detto!
Per questo, quando si alloca un blocco di memoria, conviene usare la calloc (cancellazione automatica) oppure inizializzarla a mano, cosi' eviti di considerare validi valori che stanno li per caso.
La gestione della memoria e' un argomento COMPLICATO, COMPLESSO e ROGNOSO.
Ci sono N-mila sfaccettature da prendere in considerazione, N-mila algoritmi, N-mila varianti, casi particolari, ecc.
Se vuoi fare un salto di qualita', allora usa un bel Garbage Collector: ne esiste uno anche per il C/C++
http://www.hboehm.info/gc
Nota: se implementi un tuo gestore di memoria,
ti tocca usare indici negativi