Iniziamo ora la serie di argomenti legati al funzionamento dei linguaggi di programmazione. Cercherò come al solito di limitare al minimo i tecnicismi soprattutto ora che la faccenda comincia a farsi complicata.
Per la lezione di oggi cominceremo dal più importante, vasto e primordiale aspetto: la memoria.
E’ il più importante perché è il programma stesso a risiedere in memoria e qualunque operazione vi salti in mente di fare necessita di accedere ad essa. E’ il più vasto perché come vedremo esistono vari aspetti da tenere in considerazione. Infine è primordiale perché è comune a TUTTI i linguaggi di programmazione siano essi procedurali, a oggetti, di scripting, etc…
Organizzazione della Memoria.
La memoria è un archivio con tanti cassetti. Ogni cassetto può contenere al massimo un byte (ovvero 8bit) di informazioni ed è individuato da un numero preciso chiamato indirizzo.
Le informazioni nei cassetti prendono il nome di dati. In generale un dato non è in un solo cassetto ma è diviso in 1,2,4 o 8 cassetti adiacenti cosicché il processore deve sempre sapere la dimensione base del dato che vuole leggere e ordinerà alla memoria cose del tipo “Dammi i 2 cassetti a partire dall’indirizzo XYZ” oppure “Metti questo dato nei 4 cassetti a partire dall’indirizzo ABC”.
Un dato di 1 byte è chiamato (appunto) Byte, un dato che occupa 2 byte è chiamato Word mentre un dato di 4 byte è chiamato Word.
E il nome per 8? Beh… in realtà i dati di dimensione 8 non esistono nelle architetture a 32 bit (ovvero in grado di lavorare 32 bit alla volta) e vengono rappresentati come due Word affiancate (ragion per cui spesso vengono chiamati Double). Ma l’uso dei double li ritroverete spesso quindi tanto vale far finta che esistono.
Ecco. In queste poche (spero chiare) parole è riassunta l’architettura base della memoria di un calcolatore.
Le variabili.
I linguaggi di programmazione utilizzano per l’accesso alla memoria le variabili. Esse non sono altro che nomi mnemonici a quei cassetti della memoria. La variabile “anni” è sicuramente più leggibile di qualcosa del tipo hA3B577FF.
Le variabili hanno quasi sempre un tipo. Il tipo, oltre alle sue funzioni sintattiche, serve ad indicare al calcolatore la dimensione del dato: gli interi occupano 4byte, i caratteri occupano 1byte, i float (numeri in virgola mobile) occupano 4byte e cosi via.
Heap, Stack e Text.
Ora entriamo un altro pochino nel dettaglio. Quando un programma è in esecuzione sorge la necessità di organizzare un po questi cassetti (tenere un archivio in ordine è di aiuto anche per un computer). In particolare vorremmo almeno dividere i cassetti in modo tale che il testo del programma sia separato dai cassetti che useremo per memorizzare le variabili.
Per questo motivo la memoria durante l’esecuzione viene divisa in tre parti:
- Text: La zona in cui c’è il testo del nostro programma ovvero la lista delle istruzioni che il processore eseguirà.
- Stack: La zona in cui vengono memorizzate le informazioni delle funzioni e le variabili statiche e locali.
- Heap: La zona in cui vengono memorizzate tutte le variabili dinamiche (e in un certo qual modo le variabili globali).
Riguardo al Text non c’è nulla di più da dire.
Lo stack (in italiano pila) può essere immaginato come una pila di block notes in cui noi possiamo leggere solo quello affiorante. Ogni qual volta in un programma si chiama una funzione, si prende un nuovo block notes e sulla prima pagina si annota l’ultima istruzione eseguita (per poterci tornare appena finita la funzione) e poi si lasciano bianche un certo numero di pagine corrispondenti alle variabili locali (ovvero proprie della funzione) e ai parametri della funzione.
Appare quindi evidente che quando è in esecuzione una funzione tutte le variabili locali e i parametri delle funzioni precedenti (sotto il block notes affiorante) sono illeggibili.
Per questo una variabile locale è visibile solo nella funzione in cui compare.
Inoltre nello stack le variabili hanno dimensione fissa e per questo si dicono statiche.
Lo heap (in italiano mucchio) invece può essere visto come una lavagna in cui il programma può appuntare variabili con dimensione variabile, oggetti complessi e soprattutto variabili accessibili a tutte le funzioni. Li ci sono tutte le variabili dinamiche.
Prima di utilizzare un pezzo della memoria dell’heap bisogna allocarla. Il processo di allocazione consiste in:
- “Prenotare” lo spazio necessario.
- Restituire alla funzione chiamante l’indirizzo della zona prenotata.
Una volta che la memoria non serve più la si dealloca in modo da liberare lo spazio.
Per il momento basta cosi. Riprenderemo da qui nel prossimo articolo e continueremo parlando di puntatori.