Docsity
Docsity

Prepara i tuoi esami
Prepara i tuoi esami

Studia grazie alle numerose risorse presenti su Docsity


Ottieni i punti per scaricare
Ottieni i punti per scaricare

Guadagna punti aiutando altri studenti oppure acquistali con un piano Premium


Guide e consigli
Guide e consigli


Appunti su Strutture dati dinamiche - Programmazione C, Appunti di Elementi di Informatica

Appunti chiari e sintetici su allocazione dinamica della memoria e strutture dati dinamiche (liste), con esempi guidati. Argomenti: allocazione dinamica (malloc, free), array dinamici, liste e relativi sottoprogrammi (inserimento in testa, in coda...)

Tipologia: Appunti

2013/2014

In vendita dal 17/04/2014

rickystream94
rickystream94 🇮🇹

6 documenti

1 / 12

Toggle sidebar

Questa pagina non è visibile nell’anteprima

Non perderti parti importanti!

bg1
Introduzione con esempio pratico
Il C permette di assegnare la giusta quantità di memoria (solo e solamente quella necessaria) alle
variabili del programma. In particolare l’uso della memoria allocata dinamicamente risulta utile con
gli array. Utilizzare queste caratteristiche del C permette di creare programmi altamente portabili,
in quanto utilizzano di volta in volta i valori giusti per la piattaforma.
Una piccola nota su come è organizzata la memoriaè doverosa: la memoria è divisa
sostanzialmente in due parti, una statica, che contiene tutto quello che sappiamo verrà allocato
(come una variabile int) e che si chiama Stack, ed una dinamica, cioè in cui la dimensione di
memoria per rappresentare qualche elemento del programma può cambiare durante l’esecuzione
del programma, che si chiama Heap.
Per far funzionare il programma dobbiamo includere la libreria <stdlib.h>, senza la quale queste
funzioni non potrebbero fare il loro dovere.
Presentiamo un esempio per chiarire meglio l’uso delle funzioni principali, ovvero:
malloc, free e sizeof (verranno spiegate bene nel prossimo paragrafo)
In particolare allocheremo la memoria per un array con malloc(), lavoreremo sull’array stesso ed,
infine, libereremo la memoria con free().
In questo programma possiamo notare l’uso della funzione malloc()che ritorna un puntatore
generico, corrispondente al punto di inizio, in memoria, della porzione riservata della dimensione
“intera” passata come argomento; se la memoria richiesta non può essere allocata, ritorna un
puntatore nullo.
Nel caso citato si può notare che è stato usata la funzione sizeofper specificare il numero esatto
di byte, mentre è stato effettuato un castpe r convertire il tipo di dato puntatore genericoa
puntatore ad int, questo per garantire che i puntatori aritmetici vengano rappresentati
correttamente.
Strutture dati dinamiche
10:33
Fondamenti Informatica Page 1
pf3
pf4
pf5
pf8
pf9
pfa

Anteprima parziale del testo

Scarica Appunti su Strutture dati dinamiche - Programmazione C e più Appunti in PDF di Elementi di Informatica solo su Docsity!

Introduzione con esempio pratico

Il C permette di assegnare la giusta quantità di memoria (solo e solamente quella necessaria) alle variabili del programma. In particolare l’uso della memoria allocata dinamicamente risulta utile con gli array. Utilizzare queste caratteristiche del C permette di creare programmi altamente portabili, in quanto utilizzano di volta in volta i valori giusti per la piattaforma. Una piccola nota su come è organizzata la memoria è doverosa: la memoria è divisa sostanzialmente in due parti, una statica , che contiene tutto quello che sappiamo verrà allocato (come una variabile int ) e che si chiama Stack , ed una dinamica , cioè in cui la dimensione di memoria per rappresentare qualche elemento del programma può cambiare durante l’esecuzione del programma, che si chiama Heap. Per far funzionare il programma dobbiamo includere la libreria <stdlib.h> , senza la quale queste funzioni non potrebbero fare il loro dovere. Presentiamo un esempio per chiarire meglio l’uso delle funzioni principali, ovvero: malloc , free e sizeof (verranno spiegate bene nel prossimo paragrafo) In particolare allocheremo la memoria per un array con malloc(), lavoreremo sull’array stesso ed, infine, libereremo la memoria con free(). In questo programma possiamo notare l’uso della funzione malloc() che ritorna un puntatore generico, corrispondente al punto di inizio, in memoria, della porzione riservata della dimensione “intera” passata come argomento; se la memoria richiesta non può essere allocata, ritorna un puntatore nullo. Nel caso citato si può notare che è stato usata la funzione sizeof per specificare il numero esatto di byte, mentre è stato effettuato un cast per convertire il tipo di dato “ puntatore generico ” a “ puntatore ad int ”, questo per garantire che i puntatori aritmetici vengano rappresentati correttamente.

Strutture dati dinamiche

giovedì 12 dicembre 2013 10:

Strumenti per la gestione dinamica dei dati e loro uso

Introduciamo l'argomento con un altro esempio: Abbiamo un file con dei valori, di lunghezza indefinita, e vogliamo trovare in un nuovo file i valori ordinati. Supponiamo che nel file ci stiano al più 100 interi. Chiaramente mi serve della memoria per ordinare i dati, posso utilizzare un array in questo caso di dimensione prefissata. Vediamo come implementare il main che utilizza i sottoprogrammi (si suppone già definiti) ordinaArray, stampaArray, riempiArray: Come faccio però a creare un programma che vada bene con un numero di interi non prefissato? Mi serve uno strumento che all'inizio dell'elaborazione mi dica in modo chiaro quanti dati mi serve memorizzare. Bisogna poter chiedere una quantità di memoria , usarla, e restituirla quando non serve più. Non si parlerà più di un array statico, perché è appunto di dimensione prefissata. Ecco quindi la necessità di introdurre l'allocazione dinamica della memoria. Vantaggi : poter utilizzare una quantità definita di memoria senza dichiarare un array statico di dimensioni maggiori, lasciando dello spazio in memoria non utilizzato. Vediamo come implementare questo strumento nel nostro programma: Nella dichiarazione bisognerà sostituire int dati[NUM],dim con: int dim; int *dati (Si tratta di un puntatore)

Allocazione dinamica della memoria

sabato 14 dicembre 2013 20:

Le operazioni di allocazione e cancellazione di memoria

Come già visto, le due funzioni principali sono malloc e free. La chiamata di funzione: malloc (sizeof (TipoDato) ); crea in memoria una variabile di tipo TipoDato e restituisce come risultato l 'indirizzo della variabile creata (più precisamente, il valore restituito è l 'indirizzo del primo byte riservato alla variabile ). Se p è una variabile di tipo puntatore a TipoDato, l'istruzione: p = malloc (sizeof (TipoDato) ); assegna l'indirizzo restituito dalla funzione malloc a p. Di conseguenza, dopo l'esecuzione di questa istruzione p punta alla nuova variabile perdendo il suo valore originario. N.B. La memoria viene allocata per un dato di tipo TipoDato, non per p, che invece è una variabile già esistente. In C una variabile creata dinamicamente è necessariamente anonima : a essa si può fare riferimento solo tramite puntatore; una variabile dichiarata mediante un proprio identificatore può invece essere indicata tramite l'identificatore stesso ma può anche essere puntata. Un puntatore si comporta come una normale variabile identificata (esso può essere indicato mediante identificatore o puntato da un altro puntatore). Simmetricamente, la chiamata di funzione free(p) rilascia lo spazio di memoria puntato da p; ciò significa che la corrispondente memoria fisica è resa nuovamente disponibile per qualsiasi altro uso. È importante notare che la funzione free deve ricevere, come parametro in ingresso, un puntatore al quale era stato assegnato come valore l'indirizzo restituito da una funzione di allocazione dinamica di memoria (malloc, nel nostro caso). L'uso delle funzioni malloc e free richiede l'inclusione delle librerie <stlib.h> tramite l'istruzione #include <stdlib.h> Esempio : Scrivere un sottoprogramma che ricevuta in ingresso una stringa ne crea una nuova della dimensione strettamente necessaria per ospitare il contenuto della stringa ricevuta in ingresso e la restituisce al chiamante. N.B. Il sottoprogramma stringdup è incluso nelle librerie <string.h> perciò non è necessario dichiararlo o definirlo per poterlo utilizzare nel main; in questo esempio abbiamo visto come è strutturato, ma nel prossimo esempio si può notare come esso venga utilizzato normalmente.

Allocazione dinamica della memoria

martedì 17 dicembre 2013 18:

Se volessi un programma che, sfruttando il sottoprogramma stringdup, mi permetta di allocare della memoria per dei cognomi: Liste Le liste sono uno degli argomenti più delicati da trattare, per il semplice fatto che sono uno strumento potente e versatile, ma anche molto fragile. Basti pensare che l’uso delle liste permette di impostare programmi di ordinamento in modo molto efficiente, ed offre quella dinamicità, tra l’altro tipica del C, di cui si può avere bisogno durante lo sviluppo di un programma. Una lista non è altro che una collezione di elementi omogenei, ma, a differenza dell’array , occupa in memoria una posizione qualsiasi, che tra l’altro può cambiare dinamicamente durante l’utilizzo della lista stessa; inoltre la sua dimensione non è nota a priori e può variare nel tempo (l’opposto dell’array, in cui la dimensione è ben nota e non è modificabile). Una lista può contenere uno o più campi contenenti informazioni, e, necessariamente, deve contenere un puntatore per mezzo del quale è legato all’elemento successivo. Lo spazio che viene richiesto non è solo lo spazio necessario a memorizzare il dato del tipo TipoDato, ma anche un'altra informazione che serve a puntare all'elemento successivo, quindi verrà memorizzato anche un indirizzo. Le liste sono indispensabili quando si vuole trattare una quantità di dati non stimata, non dimensionabile, e che può essere incrementata nel tempo. A causa dell'uso dei puntatori, l'accesso alla lista avviene proprio attraverso il puntatore al suo primo elemento. Nel main dovrò dichiarare quindi solo un puntatore al primo elemento della lista. Chiaramente dovrò sapere quante liste mi servono. L'indirizzo del primo elemento viene chiamato testa della lista. All'inizio di ogni programma, in fase di dichiarazione, la testa della lista sarà vuota (punta a NULL). Questo è vero per qualsiasi programma. Significa quindi che in qualsiasi lista con numero di elementi diverso da 0, la seconda parte dell'ultimo elemento ha valore NULL che assume il significato di fine lista. La lista base ha un solo campo informazione ed un puntatore, come mostrato di seguito: Elemento = informazione + puntatore che, tradotto in codice, risulta essere: N.B. Il campo *next rappresenta un puntatore al tipo strutturato che si sta definendo, perciò non potrò usare come tipo di dato tipoLista_t poiché ancora non è stato dichiarato! mercoledì 18 dicembre 2013 18:

Nel caso di lista vuota, esso non costituisce un caso da gestire a parte, poiché head inizialmente è NULL ma il suo valore poi viene sovrascritto da tmp , perciò il successivo elemento della lista diventa il valore che ho appena allocato. Se voglio fare il sottoprogramma che inserisce un elemento all'inizio della lista , dovrà restituire il nuovo indirizzo di dove inizia la lista: Nel main vediamo la chiamata al sottoprogramma: La rappresentazione grafica dell'inserimento in testa è la seguente: giovedì 19 dicembre 2013 11:

Controllo esistenza di un elemento in una lista Esercizio : sottoprogramma che ricevuti in ingresso una lista e un valore intero n restituisce il numero di volte che n compare nella lista. Innanzitutto mi serve qualcosa per scandire la lista, un indicatore per sapere a quale elemento della lista sto guardando in questo momento. Non si tratta di un ciclo a conteggio, perché in generale non so quanti elementi ho nella lista. Tuttavia si utilizza comunque il ciclo for per la scansione: Inserimento in coda alla lista Si parte sempre dall'inizio e ci fermiamo quando p->next==NULL Eliminare primo elemento della lista Esercizio : sottoprogramma che riceve in ingresso una lista ed elimina il primo elemento della lista: Se la lista fosse vuota, non ci sarebbe nulla da fare, altrimenti bisogna fare in modo che la testa punti al secondo, poi liberare la memoria occupata dal primo: Nel main la chiamata al sottoprogramma sarà: head = eliminaPrimo(head); giovedì 19 dicembre 2013 12:

Procediamo con la definizione del corpo della funzione crea_lista() , la quale crea due puntatori ad ilist_t, uno di nome p e l’altro di nome punt ; queste due variabili serviranno per scorrere la lista, infatti p è il puntatore al primo elemento della lista, mentre punt è un puntatore ausiliario che permette di scorrere la lista; la variabile i è l’indice del ciclo, mentre n serve a memorizzare il numero degli elementi che si intende inserire. Vediamo che è stata usata la funzione malloc() insieme a sizeof per allocare dinamicamente la memoria necessaria ad un elemento della lista; il valore ritornato viene castato ad un puntatore allo spazio allocato, che viene assegnato a p. Assumendo che sia stato inserito un valore di n (il numero di elementi della lista) uguale a 3; nella prima iterazione, viene creato il primo elemento della lista con il codice sopra esposto e viene assegnato (tramite tastiera), ad esempio, il valore 5; Successivamente viene creato un altro puntatore alla prima posizione, di nome “punt”, tale puntatore ausiliario, servirà per scorrere gli elementi della lista e mantenere il collegamento all’ultimo elemento inserito. venerdì 20 dicembre 2013 17:

Proseguendo con l’esecuzione del codice, arriviamo ad inserire il secondo ed il terzo elemento (grazie al ciclo for ): Per prima cosa viene creato un altro oggetto della lista, identificato con punt->pun: poi punt, il puntatore ausiliario, viene fatto puntare, non più al primo elemento, bensì al secondo, all’atto pratico punt diventa il puntatore dell’oggetto da lui puntato (punt = punt->pun;). Quindi viene inserito il campo informazione dell’elemento tramite l’input da tastiera dell’utente; in questo caso viene inserito il valore 20; La procedura, ovviamente, si ripete per il terzo elemento, e, se vi fossero stati altri elementi da aggiungere, si sarebbe ripetuta fino all’inserimento dell’ultimo elemento; venerdì 20 dicembre 2013 17: