• Programmazione Android
  • CORSI ONLINE
  • Web Agency

Logo

Corsi di programmazione web e mobile online
Navigation
  • Home
  • CORSI ONLINE
  • Tutorial Pratici
  • GUIDE COMPLETE
    • Corso completo di C
    • Corso videogame con Cocos2d
    • Programmazione Cocoa Touch
  • Sezioni
    • Libri e manuali
    • Tips & Tricks
    • Risorse utili
    • Strumenti di Sviluppo
    • Materiale OpenSource
    • Framework
    • Guide Teoriche
    • Guide varie
    • Grafica e Design
    • iPad
    • News
    • Video Tutorial
    • Windows Phone
  • Pubblicità
  • About
    • Chi siamo
    • Pubblicazioni
    • Collabora
    • Sostieni devAPP

14. I Puntatori – La gestione dinamica della memoria – parte 3

By IgnazioC | on 18 Maggio 2011 | 12 Comments
Corso completo di C

corso-completo-c-i-puntatori-parte-2-gestione-dinamica-della-memoria-00 Ciao a tutti, eccoci giunti alla quattordicesima lezione del nostro corso completo di programmazione in C. Anche oggi parleremo dei puntatori e nello specifico approfondiremo alcuni aspetti legati alla gestione dinamica della memoria, prima di partire, però, facciamo un pò il punto di quello che abbiamo detto e visto fino ad ora sui puntatori:

 

  • Sono un tipo di variabile
  • Devono essere tipizzati (int, char etc.)
  • Possono essere usati con l’operatore di dereferenziazione ( * ) per accedere al contenuto della cella di memoria puntata
  • L’operatore di indirizzamento ( & ) restituisce l’indirizzo al quale una variabile è memorizzata, quindi può essere considerato il duale dell’operatore ” * “. (Cosa succede ad esempio scrivento ” *(&nome_variabile) ” ??
  • Possono essere inseriti tra i parametri delle funzioni, riuscendo così a superare il problema del passaggio dati per valore.
  • Sono alla base della creazione di strutture statiche come array, matrici e matrici multidimensionali.
  • Con l’aritmetica dei puntatori possiamo modificare la cella di memoria puntata dal puntatore per muoverci agevolmente su di essa.

Ma se dovessi scegliere un solo motivo per il quale sono stati inventati i puntatori, sceglierei proprio quello di cui parleremo in questa lezione: l’allocazione dinamica della memoria.

Negli esempi che abbiamo scritto nella precedente lezione, quando abbiamo parlato di strutture dati come array e matrici, abbiamo detto che per dichiarare un array di 10 elementi (indicizzati da 0 a 9) bisogna scrivere:

int nomeArray[10];

in questo modo il compilatore riserverà ad ogni esecuzione del nostro programma 10 * (dimensione di un intero) byte in memoria per questa variabile e sarà accessibile tramite:

nomeArray[n]; //n compreso tra 0 e 9

Questo approccio ha il difetto di allocare sempre lo spazio per 10 interi in memoria, il che può andar bene se il valore “10” è fisso e indipendente dall’esecuzione, ma supponiamo che quel parametro dipenda dalle scelte dell’utente, come potremmo fare?

Supponiamo per esempio di voler realizzare un programma che calcoli alcuni valori statistici su un numero di campioni scelti dall’utente memorizzandoli in un array. (in questo esempio calcolo solo la somma)

#include 

int somma(int *array, int size){

  int i; //variabile utilizzata per i cicli.

  int somma = 0;        //variabile per memorizzare la somma.

  for (i = 0; i < size; i++){

    somma = somma + array[i]; //aggiungo alla somma parziale il valore dell'array in posizione i

  }

  return somma; //restituisco il valore della somma.

}

int main (int argc, const char * argv[]) {

  int qt_numeri;

  int numero;

  int i;

  int arrayDiNumeri[255];  //array dove memorizzare i numeri insiti dall'utente.

  printf("Inserisci la quantià di numeri: ");

  scanf("%d",&qt_numeri);

  printf("Dovrai inserire %d numeri:",qt_numeri);

  for (i = 0 ; i < qt_numeri ; i++ ) {

    scanf("%d",&numero);

    arrayDiNumeri[i] = numero;

   }

  printf("la somma è: %d", somma(arrayDiNumeri, qt_numeri));

  return 0;

}

A questo punto del corso non dovreste avere grosse difficoltà a capire questo codice, ma come potete ben notare c’è un problema enorme: siamo costretti a dichiarare un array di dimensione 255 perché non sappiamo in anticipo quanti numeri l’utente deciderà di inserire… quindi adremo ad occupare 255 * (dimensione di un intero) ciascuna volta che l’utente eseguirà il programma…certo occupare circa 1000 byte (1 kb) può non sembrare un grosso spreco, vista la quantità in gigabyte di memoria che hanno a disposizione i nostri computer, ma per realizzare un programma efficiente bisogna essere come accaniti risparmiatori, intenti a rosicchiare byte dove è possibile! Ma c’è un errore ancora più grave, cosa succede se l’utente decide di inserire 256 numeri? all’interno del ciclo for tenteremmo di scrivere nell’allocazione arrayDiNumeri[255] ma l’ultima allocazione effettivamente utilizzabile è arrayDiNumeri[254] come si vede in questa immagine:

corso-completo-c-i-puntatori-parte-2-gestione-dinamica-della-memoria-01

e questo è un errore gravissimo, perché non abbiamo idea di che cosa sia memorizzato in quella allocazione di memoria, magari stiamo sovrascrivendo un dato importante, infatti molto spesso un errore di questo tipo porta al crash dell’intero programma.

Sottolineo il termine molto spesso e non ho detto sempre! Come abbiamo avuto modo di notare più volte il linguaggio C suppone che il programmatore sappia quello che sta facendo, quindi si limita ad “eseguire gli ordini” senza fare troppi controlli, e purtroppo l’errore di scrivere in un’allocazione di memoria errata può alcune volte passare inosservata anche agli sguardi molto attenti. Per darvi un’idea anche una vecchia versione di SSH era afflitta da un errore simile, con le conseguenze del caso.

Torniamo al problema originario, come facciamo ad allocare un’array di dimensione variabile a seconda delle scelte dell’utente? In C esiste l’allocazione dinamica della memoria che permette proprio di fare quanto richiesto.

È d’obbligo fare una digressione su come viene gestita la memoria all’interno del computer, ma ci accontentiamo di sapere che viene divisa in due parti, lo stack dove vengono allocate le variabili statiche ovvero tutte quelle dichiarate fino ad adesso e l’heap dove vengono allocate le variabili allocate dinamicamente. La differenza sostanziale è che lo stack è gestito come una pila, tutte le variabili vengono messe una sull’altra, quando una funzione viene invocata nello stack vengono allocate tutte le variabili che appartengono a quella funzione, quando una funzione termina la parte di stack viene svuotata e così viene liberata la memoria. L’heap invece ha una funzionalità diversa, per ottenere dello spazio nell’heap bisogna utilizzare delle funzioni particolari (che vedremo tra un attimo) questo spazio, però, non viene rilasciato quando la variabile esce dallo scope, dovremmo invece rilasciarla esplicitamente.

La funzione principale da utilizzare per ottenere dello spazio nell’heap è la malloc, il cui prototipo è il seguente:

void *malloc(size_t size);

e la pagina di manuale recita:

The malloc() function allocates size bytes of memory and returns a pointer to the allocated memory.

La funzione è dichiarata dentro la libreria stdlib.h, quindi ricordiamoci di inserire la direttiva #include in cima ai nostri programmi se intendiamo utilizzarla.

Ma che tipo di parametro richiede? è la prima volta che incontriamo il tipo di dato size_t ma niente paura è semplicemente un tipo di dato come un altro e si usa quando ci si riferisce alla dimensione in byte di un oggetto in memoria. Difficilmente però ci troveremo a dichiarare delle variabili di tipo size_t, più probabilmente utilizzeremo la funzione sizeof() che restituisce proprio la dimensione dell’argomento espressa in size_t.

Con un esempio tutto risulterà più chiaro: dichiariamo un puntatore e facciamolo puntare ad un’allocazione di memoria dell’heap in grado di ospitare 10 interi

int *array;

array = malloc( 10 * sizeof(int) );

Dichiariamo un array nell’heap in grado di contenere 5 caratteri:

char *array;

array = malloc(5 * sizeof(char));

A questo punto le strutture possono essere utilizzare come dei normali array statici, accedento alle singole variabili tramite array[n]

Ci sono due cose da non dimenticare quando si ha a che fare con l’allocazione dinamica della memoria:

  1. Controllare il valore di ritorno della funzione malloc
  2. Ricordarsi di liberare esplicitamente la memoria allocata quando non più necesaria.

La funzione malloc restituisce NULL se non c’è sufficiente memoria disponibile per essere allocata, questo non succede molto spesso, ma in alcuni casi potrebbe accadere, quindi è buona norma assicurarsi sempre che il puntatore restituito dalla malloc sia diverso da NULL.

Per liberare lo spazio allocato tramite la funzione malloc bisogna richiamare la funzione free() passandole come parametro proprio il puntatore restituito dalla malloc. Da notare che per liberare lo spazio abbiamo bisogno del puntatore, quindi lo spazio va liberato mentre si è nello scope della variabile, usciti dallo scope si ha quello che si chiama “memory leak” (link) ovvero della memoria allocata, inutilizzabile e non più liberabile.

Vediamo quindi il precedente esempio come viene modificato utilizzando la gestione dinamica della memoria:

#include 

#include 

int somma(int *array, int size){

  int i;

  int somma = 0;

  for (i = 0; i < size; i++){

    somma = somma + array[i];

  }

  return somma;

}

int main (int argc, const char * argv[]) {

  int qt_numeri;

  int numero;

  int i;

  int *arrayDiNumeri; //dichiariamo come semplice puntatore

  printf("Inserisci la quantià di numeri: ");

  scanf("%d",&qt_numeri);

  printf("Dovrai inserire %d numeri:",qt_numeri);

//alloco dinamicamente la memoria  

arrayDiNumeri = malloc(qt_numeri * sizeof(int));

//verifico che il puntatore non sia null,

// la condizione è più spesso abbreviata in questo modo "if (! arrayDiNumeri)"

if (arrayDinumeri == NULL) {

//esco dal programma riportando un errore.

return -1;

}

  for (i = 0 ; i < qt_numeri ; i++ ) {

    scanf("%d",&numero);

    arrayDiNumeri[i] = numero;

   }

  printf("la somma è: %d", somma(arrayDiNumeri, qt_numeri));

//libero la memoria allocata  

free(arrayDiNumeri);

  return 0;

}

Per tutti coloro che hanno già avuto a che fare con la programmazione in objective-c, vorrei far notare che la dichiarazione di un nuovo oggetto, ad esempio tramite l’istruzione

NSString *myName = [[NSString alloc] init];

praticamente è la verione “evoluta” della malloc che abbiamo appena visto, viene riservato infatti nell’heap lo spazio sufficiente a memorizzare un oggetto di tipo NSString. Addirittura per conformità con il linguaggio hanno mantenuto il simbolo ” * ” davanti al nome della variabile, proprio ad indicare che si tratta di un puntatore.

Una variante sul tema

Vorrei precisare che la funzione malloc non è l’unica in grado di allocare della memoria nell’heap, una valida alternativa è la funzione:

void * calloc(size_t count, size_t size);

La sintassi è molto simile, ma questa prevede due parametri separati, in pratica per dichiarare un array da 10 interi bisogna scrivere:

int *array;

array = calloc(10, sizeof(int));

Un’ulteriore differenza è che la calloc restituisce la zona di memoria già azzerata, mentre la malloc restituisce la memoria così com’è.

Esiste poi la funzione realloc() il cui prototipo è il seguente:

void *realloc(void *ptr, int dim_totale)

Il suo scopo è quello di modificare la dimensione di un blocco di memoria puntato da ptr per farlo diventare di dim_totale bytes.

Se dim_totale è 0, l’invocazione è equivalente ad una free(ptr). Naturalmente il valore di ptr deve provenire da una precedente invocazione di malloc() o calloc() o realloc()

Come ormai consueto vi lascio con qualche esercizio, alla prossima!!

Esercizi

  1. Scrivere una funzione che scambi il valore di due variabili, passate come parametro. (hint: utlizzare i puntatori come parametro non servono array)
  2. Scrivere una funzione che chieda all’utente quanti numeri inserire, quindi allochi dinamicamente un’array della dimensione corretta e prenda in input tutti i valori, al termine dell’inserimento calcoli la media dei valori inseriti.
  3. Scrivere una funzione che presi in input due valori ne restituisca un terzo lungo il doppio ottenuto dalla concatenazione dei due array in input.
  4. Scrivere una funzione che preso in inut un array lo restituisca in ordine inverso.
  5. Scrivere un programma che allochi dinamicamente lo spazio per una matrice 10×10.

Letture consigliate:

C-Corso-Completo-di-Programmazione-kernighan-ritchie-devAPP Il linguaggio C. Principi di programmazione e manuale di riferimento (Accademica)
Brian W. Kernighan – Dennis M. Ritchie
Editore: Pearson | Lingua: Italiano | Brossura: 313 pagine
Prezzo Listino: EUR 27,00
Prezzo Promozione: EUR 22,95 con Spedizione gratuita

C-Corso-Completo-di-Programmazione-Deitel-Deitel-devAPP C. Corso completo di programmazione
Paul J. Deitel – Harvey M. Deitel
Editore: Apogeo | Lingua: Italiano | Brossura: 640 pagine
Prezzo Listino: EUR 39,00
Prezzo Promozione: EUR 33,15 con Spedizione gratuita

Share this story:
  • tweet

Tags: corso completo di Ccorso di Cesempi codice programmazione Cesercizi programmazione Cgestione dinamica della memoria in CI puntatori in CIgnazio Calò

Recent Posts

  • Parte il percorso programmatori iOS in Swift su devACADEMY.it

    20 Dicembre 2017 - 0 Comment
  • Android, crittografare dati velocemente con Encryption

    24 Settembre 2018 - 0 Comment
  • Sql2o, accesso immediato ai database tramite Java

    3 Settembre 2018 - 0 Comment
  • Okio, libreria per ottimizzare l’input/output in Java

    27 Agosto 2018 - 0 Comment

Related Posts

  • iSketch: la lavagna magica di Ignazio Calò disponibile GRATUITAMENTE in App Store

    29 Agosto 2011 - 2 Comments
  • 17. La gestione dei file in C

    22 Agosto 2011 - 4 Comments
  • Uno sguardo a Unit test: impariamo a prevenire ore e ore di debug durante lo sviluppo di Applicazioni iOS

    8 Agosto 2011 - 2 Comments

Author Description

12 Responses to “14. I Puntatori – La gestione dinamica della memoria – parte 3”

  1. 18 Maggio 2011

    cikpis

    f (arrayDinumeri != NULL) {

    //esco dal programma riportando un errore.

    return -1;

    }

    C’è un’errore!
    La condizione dell’if deve avere come operatore == e non !=…
    Per il resto tutto ok!

  2. 19 Maggio 2011

    ignazioc

    Confermo!! modifico subito l’articolo

  3. 24 Maggio 2011

    andrea90rm

    sempre nella parte finale..hai scordato il contenuto degli include

  4. 30 Maggio 2011

    peppe

    non ci abbandonare……………..

  5. 31 Maggio 2011

    Ignazioc


    andrea90rm:

    sempre nella parte finale..hai scordato il contenuto degli include

    purtroppo i caratteri “” sono un problema in html 🙁

  6. 31 Maggio 2011

    Ignazioc


    peppe:

    non ci abbandonare……………..

    non vi ho abbandonato 🙂 sono sempre qui!

  7. 26 Giugno 2011

    Luca

    forse mi sono perso un pezzo ma si inizia a parlare di scope….che cosa vuol dire?

  8. 4 Ottobre 2011

    Nicola

    Ho capito che si potrebbe verificare un errore nel caso si dichiari un array di capacità più piccola di quella dichiarata come nel primo esempio.
    Tale problema dovrebbe essere risolto utilizzando la memoria dinamica nell’heap…perchè?
    Solo perchè vi è la possibilità di controllare tale errore con il NULL?

    Grazie

    Nic

  9. 4 Ottobre 2011

    ignazioc

    non ho capito bene il tuo dubbio. Un array statico avrà sempre la stessa dimensione, scelta nel momento in cui scrivi il programma. Alcune volte si ha la necessità di array la cui dimensione sia variabile nel tempo, per questo motivo si dichiarano “array dinamici” (che poi sono sempre puntatori) che vengono memorizzati nell’heap.

  10. 5 Ottobre 2011

    Nicola

    Perchè posso dichiarare malloc(variabile_che _inserisce_utente * sizeof(int))?

  11. 14 Febbraio 2013

    Corso di programmazione in C – I Puntatori – La gestione dinamica della memoria – parte 3 | devAPP | DiFuscoFrancesco.it

    […] http://www.devapp.it/wordpress/14-i-puntatori-la-gestione-dinamica-della-memoria-parte-3.html […]

  12. 9 Gennaio 2014

    abe

    Noooooooooooooooooooooooo…
    non ci sono le listeeeeeeeeeeeeeee!!

    E’ l’unico sito da cui capisco le cose.

    LE LISTE! LE LISTE! LE LISTE!

Leave a Reply

Your email address will not be published. Required fields are marked *


*
*

Corso online di programmazione android e java

SEZIONI

  • Android
  • Comunicazioni
  • Contest
  • Corsi ed Eventi
  • Corso completo di C
  • Corso programmazione videogiochi
  • Framework
  • Grafica e Design
  • Guida rapida alla programmazione Cocoa Touch
  • Guide Teoriche
  • Guide varie
  • iPad
  • Le nostre applicazioni
  • Libri e manuali
  • Materiale OpenSource
  • News
  • Pillole di C++
  • Progetti completi
  • Risorse utili
  • Strumenti di Sviluppo
  • Swift
  • Tips & Tricks
  • Tutorial Pratici
  • Video Tutorial
  • Windows Phone

Siti Amici

  • Adrirobot
  • Allmobileworld
  • Apple Notizie
  • Apple Tribù
  • Avvocato360
  • Blog informatico 360°
  • bubi devs
  • fotogriPhone
  • GiovaTech
  • iApp-Mac
  • iOS Developer Program
  • iPodMania
  • MelaRumors
  • Meritocracy
  • SoloTablet
  • TecnoUser
  • Privacy & Cookie Policy
©2009-2018 devAPP - All Rights Reserved | Contattaci
devAPP.it è un progetto di DEVAPP S.R.L. - Web & Mobile Agency di Torino
Str. Volpiano, 54 - 10040 Leini (TO) - C.F. e P.IVA 11263180017 - REA TO1199665 - Cap. Soc. € 10.000,00 i.v.

devACADEMY.it

Vuoi imparare a programmare?

Iscriviti e accedi a TUTTI i corsi con un’unica iscrizione.
Oltre 70 corsi e migliaia di videolezioni online e in italiano a tua disposizione.

ISCRIVITI SUBITO