Premi qui per scaricare il PDF
Premi qui per scaricare il PDF
Allocazione Dinamica
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.
malloc (numero di byte da allocare)
void *malloc(int)
La funzione malloc ritorna un puntatore atipizzato (puntatore a nessun tipo), che deve essere tipizzato al momento della chiamata con un casting. Il puntatore restituito corrisponde al punto di inizio, in memoria, della porzione che si vuole riservare, passata come argomento; se la memoria richiesta NON può essere allocata, ritorna un puntatore nullo.
Spazio occupato in memoria dai diversi tipi:
- char → occupa 1 byte in memoria;
- int → occupa 2 byte in memoria;
- float → occupa 4 byte in memoria.
int a;
int *punt;
a = 50; //Mi servono 50 interi
punt = malloc(a * 2); //a * numero byte occupati dal tipo della variabile a
//OPPURE
punt = (int*) malloc(a * sizeof(int));
Sizeof(nome tipo) → Restituisce la dimensione occupata in memoria, del tipo passato come parametro.
*punt = 22;
Inserisce 22 nei primi due byte (nel primo intero) di memoria allocata.
*punt + 1 = 25;
Inserisce 25 nell’ intero successivo in memoria.
Esempio
Programma che chiede all’utente quanti voti vuole acquisire. Acquisire il numero di voti inserito dall’utente
#include < malloc.h>
int main()
{
int quanti, i;
int *voti;
printf("Quanti studenti?\\n");
scanf("%d", &quanti);
voti = (int) malloc(quanti * sizeof(int));
for (i = 0; i < quanti; i++)
{
printf("Immetti il voto del %d studente:\\n", i+1);
scanf("%d", &(*(voti+i))); //corrisponde a scanf("%d", voti+i);
}
}
Esempio 2
typedef struct prova
{
int umido;
float temperatura;
}prova;
int main()
{
int quanti, i;
prova * punt;
scanf ("%d", &quanti);
punt = (prova*) malloc(quanti * sizeof(prova));
for (i = 0; i < quanti; i++)
{
printf("Immetti i dati\\n");
scanf("%d %f", &(*(punt+i)) -> umido, &(*(punt+i) -> temperatura);
}
}
Lista
Non so a priori quanti dati avrò.
La funzione malloc() presuppone che l’utente sappia quanti elementi vuole inserire nell’array. Qualora l’utente NON dovesse sapere quanti elementi vuole registrare, si utilizza una struttura che viene creata, man mano che vengono inseriti i dati.
Lista semplice → Insieme di strutture collegate l’una all’altra attraverso un campo puntatore. Il primo elemento della lista è chiamato “testa della lista“.
typedef struct studente
{
int voto;
char nome[20];
struct studente *next; //Puntatore alla struttura (per la lista)
}studente;
studente *testa; //Testa della lista - primo elemento della lista
int main()
{
testa = (studente*)malloc(sizeof(studente));
testa -> voto = 20; //scanf("%d", &testa->voto); equivale &(*testa.voto);
testa -> nome = "Rossi"; //gets(testa->nome); equivale a *testa.nome
testa -> next = NULL; //non punta a niente, non c'è nessun elemento successivo
//NULL equivale a 0.
}
VANTAGGI
- Posso facilmente cancellare un elemento da una lista, semplicemente spostando il puntatore dell’elemento precedente a quello che voglio cancellare, all’elemento successivo a quello che voglio cancellare.
- Posso facilmente ordinare una lista, spostando il puntatore dell’elemento più piccolo a quello più grande.
SVANTAGGI
- Occupa più spazio;
- Non si può prevedere il tempo di accesso all’istruzione seguente.
Operazioni eseguibili su una lista
- Inserimento
- In testa → Inserisce il nuovo elemento all’inizio;
- In coda → Inserisce il nuove elemento alla fine;
- In mezzo → ordinamento
- Ricerca
- Cancellazione
Inserimento in testa
Se la testa punta già a degli elementi (lista parzialmente piena):
- Allocare nuovo elemento usando una puntatore temporaneo;
- Assegno al campo “next” del nuovo elemento, l’indirizzo che è nella “testa“;
- Copio il puntatore temporaneo nella “testa“.
Esempio
Inserire un elemento in cima alla lista e restituire 1 se riesce ad inserirlo e 0 se dovesse avere poca memoria da allocare.
typedef struct prova
{
int dato;
struct prova *next;
}prova;
prova *testa;
int main()
{
int risultato;
risultato = insTesta();
return 0;
}
int insTesta()
{
prova *temp; //Passo 1
if (temp = (prova*)malloc(sizeof(prova))) //Controllo se la malloc viene fatta
{
temp->next = testa; //Passo 2
testa = temp; //Passo 3
return 1; //Traccia dell'esercizio
}
else
return 0;
}
Inserimento in coda
Se la testa punta già a degli elementi (lista parzialmente piena):
- Allocare nuovo elemento usando una puntatore temporaneo;
- Secondo puntatore che deve scandire la lista ed arrivare all’ultimo elemento;
Se la testa non ha nessun elemento all’interno (lista vuota):
int insCoda()
{
prova *temp, *t1;
if (temp = (prova*)malloc(sizeof(prova)))
{
//LISTA VUOTA
if(!testa) //Se la testa è vuota => testa == NULL
{
temp -> next = testa;
testa = temp;
return 1;
}
//LISTA PIENA
else //La lista è piena, inserimento in coda
{
//Punto t1 all'ultimo elemento
for (t1 = testa; t1->next; t1 = t1 -> next); //Scandisce tutti gli elementi della lista /t1->next corrispone a t1->next != 0
temp -> next = 0; //il nuovo elemento punta a 0
t1 -> next = temp;
return 1;
}
}
else //La malloc non va a buon fine
return 0;
}
Variante Lista piena – Inserimento in coda:
- Vado fino alla fine;
- Faccio la malloc sul campo “next” dell’ultimo elemento della lista;
t1-> next = (prova*)malloc(sizeof prova)
Stampare una lista
void stampalista(L1 nome)
{
t1 = nome;
while(t1!=NULL) {
printf("%d ", t1 -> intero1);
t1=t1->next;
}
Ricercare un elemento nella lista
typedef struct prova
{
int dato;
struct prova *next;
}prova;
prova *testa;
int main()
{
prova *elem;
elem = cerca(5);
return 0;
}
prova *cerca (int chi)
{
prova *temp;
for (temp = testa; temp && temp -> dato != chi; temp = temp->next); //temp corrisponde a temp != 0
return temp;
}
Cancellare un elemento dalla lista
Devo copiare il “next” dell’elemento da cancellare nel “next” dell’elemento precedente.
Se lo cancella restituisce 1, altrimenti se non cancella niente restituisce 0.
int cancella (int chi)
{
prova* temp, *t1;
if (!testa) //Se la testa è vuota
return 0;
if (testa -> dato == chi) //Se devo cancellare il primo elemento
{
t1 = testa;
testa = testa -> next;
free (t1);
return 1;
}
for (t1 = testa, temp = t1->next; temp && temp->dato != chi; t1 = t1->next, temp = temp->next);
/*Ricerco l' elemento da cancellare nella lista, sposto entrambi i puntatori in modo che
ci sia sempre un puntatore che punta al valore successivo e uno che punta al valore precedente*/
if(!temp) //Se non trovo l'elemento (temp == 0)
return 0;
//Cancello l'elemento
t1->next = temp -> next;
free (temp);
return 1;
}
Questo metodo è valido, con opportune modifiche, anche per l’inserimento in mezzo.
Inserisco 7 nei due campi successivi al campo a cui temp sta puntando.
Esempio:
temp punta al 3° campo, in questo modo inserisco 7 nel 5° campo, senza spostare temp.
temp -> next -> next -> dato = 7;
Esempio
Se voglio inserire il valore del campo dato di temp nei due campi successivi:
temp -> next -> next -> dato = temp->dato;