lunedì 15 febbraio 2016

Memoria e puntatori (base)


Dedico questo primo post al meccanismo di gestione della memoria del calcolatore che dovrebbe essere tra i primi argomenti da affrontare per un programmatore junior, ma il più delle volte viene ignorato perché ostico.

Ritengo l'argomento molto importante perché permette di acquisire quella forma mentis che sarà utile anche con linguaggi che non fanno uso dei puntatori.

Una volta acquisita famigliarità con il loro funzionamento ci si accorgerà che tutti i linguaggi, pur con sintassi diverse, in fondo fanno sempre la stessa cosa.

Albert Einstein disse: Se non lo sai spiegare in modo semplice, non l’hai capito abbastanza bene.

Quindi il mio scopo qui è farvi capire in che modo si accede alla memoria utilizzando i puntatori senza mal di testa.

Un'immagine concettuale della memoria di un calcolatore è molto simile a un foglio Excel in cui ogni cella ha un proprio indirizzo espresso in notazione esadecimale




Quest'altra immagine concettuale mostra nel dettaglio l'organizzazione di una cella di memoria di una variabile di nome A


Il linguaggio che utilizzerò per gli esempi è il C++.

La sintassi di questo linguaggio prevede l'utilizzo di due operatori per la gestione della memoria: * e &.

Negli esempi utilizzerò anche la keyword "cout" che insieme all'operatore << permette di stampare a video le informazioni.



Operatore & (indirizzo di)



Cominciamo con il seguente esempio, con questa istruzione sto dichiarando una variabile di nome A e di tipo int a cui viene assegnato il numero 5.


int A = 5;

Poniamo che in esecuzione, il calcolatore assegni ad A l'indirizzo 0x00 dell'immagine precedente, l’operatore & permette  di utilizzare l’indirizzo  di una variabile:



cout << &A;  // stampa a video 0x00 (in un calcolatore reale l'indirizzo sarà diverso)



Operatore *(puntatore a)


L’operatore  *(puntatore a) permette  invece di utilizzare o cambiare il dato  di una cella ad un certo indirizzo di memoria.

Dire semplicemente “variabile puntatore” è concettualmente sbagliato, perché ogni variabile ha un suo tipo qundi la frase corretta è dire “variabile puntatore di tipo X ” dove per X  s’intende il tipo dichiarato.

Per enfatizzare questo, in fase di dichiarazione di una variabile puntatore, preferisco posizionare l’operatore  *  vicino al tipo dichiarato e non vicino alla variabile 

Per spiegare meglio l'uso dei due operatori riporto di seguito un esempio completo con i commenti di quello che succede in memoria ad ogni passo.

Nel codice ho utilizzato la keyword endl (end line) che serve per stampare il carattere di ritorno a capo

e System("clear") che cancella il testo presente in console.



#include <iostream>

using namespace std;

int main()
{
/*
Il calcolatore assegna ad A l’indirizzo della cella di memoria 0x00
e memorizza il valore 5
*/  
 int A =  5;
/* 
Con l’operatore & ottengo l’indirizzo di  A quindi l’istruzione 
stampa a video l'indirizzo 0x00 
*/
   cout << &A << endl;
/*
Qui di seguito viene dichiarata la variabile B, puntatore di 
tipo int, a cui il calcolatore assegna la cella all'indirizzo 0x01, 
poi tale  variabile viene utilizzata per memorizzare l'indirizzo di A
*/
  int*  B  =  &A;  //il calcolatore assegna a B la cella all’indirizzo 0x01, 
                   //in tale cella, viene memorizzato  l’indirizzo di A 0x00
/* 
La seguente istruzione provoca un errore in compilazione !
Questa riga commentata è stata inserita per sottolineare che B 
è una variabile di tipo int* (int puntatore)  e come  tale  può ricevere 
come valore solamente un indirizzo  
*/ 

//B = 5;   //Errore ! B è di tipo int* (int puntatore) non è di tipo int

/* 
Questa istruzione stampa a video il dato contenuto nella cella il cui indirizzo 
è memorizzato  in B.
Poiché in B è stato precedentemente memorizzato  l’indirizzo di A 
verrà stampato il valore  5
*/
 cout << *B << endl; //stampa il dato della cella all’indirizzo 
                     //contenuto in B cioè  5
/*
B è una variabile di tipo int*(int puntatore) che memorizza indirizzi, 
poiché è una variabile però, anche ad essa il calcolatore assegna un indirizzo.
Questa istruzione stampa a video l'indirizzo di B
*/
 cout << &B << endl;  // & ottiene l’indirizzo di B assumo che sia 0x01
/*
In questo caso invece viene stampato il dato contenuto in B,  
ma essendo B un puntatore che memorizza solo indirizzi +
e visto che in precedenza in esso è stato memorizzato l’indirizzo 
della variabile A il dato che contiene  è  0x00
*/
 cout << B << endl; //stampa il dato contenuto in B,  
                    //B è un puntatore che memorizza l’indirizzo di A quindi 0x00

/*
Il valore alla cella puntata da B viene cambiato da 5 a 10, 
e poiché B punta alla cella di memoria di A, è il valore di A che viene cambiato.
*/

 *B = 10; //cambia il dato della cella a cui punta B, cioè il valore della cella di A

 cout <<"Valore valore di A: "<< A << endl;
 cout <<"Valore contenuto nella cella puntata da B: "<< *B << endl;

 return 0;
}

Poiché un’immagine vale più di mille parole, concludo il post inserendo di seguito delle immagini che mostrano a sinistra la linea di codice in esecuzione e a destra quello che succede in memoria:


Eseguendo la linea 9 viene creata la variabile A  a cui il calolatore assegna un indirizzo e nella quale viene memorizzato il valore 5


Eseguendo la  linea 11 viene stampato a video l'indirizzo di A



Eseguendo la linea 13 avvengono due cose:

1. viene creata la variabile B di tipo int* (int puntatore)
2. ad essa viene assegnato l'indirizzo di A utilizzando l'operatore &



Eseguendo le linee 15-16-17 vengono stampati i dati a video



Eseguendo la linea 19 il valore alla cella puntata da B viene cambiato da 5 a 10, e poiché B punta alla cella di memoria di A, è il valore di A che viene cambiato.


In seguito pubblicherò qui un link dal quale potete scaricare il post in versione PDF.


Per realizzare questo post ho utilizzato i seguenti strumenti:

Paint.NET: http://www.getpaint.net/index.html
Emulatore e compilatore C++ online: http://www.tutorialspoint.com/compile_cpp11_online.php
SyntaxHighlighter: https://github.com/syntaxhighlighter/syntaxhighlighter/


ATTENZIONE!
Quanto presente in questo post viene rilasciato con licenza
Attribuzione - Non commerciale - Non opere derivate 3.0 Italia (CC BY-NC-ND 3.0 IT)
http://creativecommons.org/licenses/by-nc-nd/3.0/it/

Eventuali segnalazioni di errori ,dubbi, o opinioni potete inserirli nei commenti