Sie sind auf Seite 1von 58

Puntatori – Parte II

Allocazione dinamica della memoria

„ Come accennato, in C è possibile


memorizzare informazioni in zone di
memoria allocate dinamicamente
„ L’allocazione avviene mediante funzioni della
libreria standard *alloc che riservano un certo
spazio
„ Lo spazio viene allocato nella zona di memoria
denominata heap
„ Come abbiamo visto, l’heap sfrutta lo spazio
libero così come lo stack

1
Allocazione dinamica: vantaggi

„ Mi consente di decidere a runtime la


dimensione di una struttura dati
„ Ma soprattutto, di variare dinamicamente la
dimensione dello spazio di memoria allocato
per una struttura
„ Richiedendo nuovo spazio quando la
dimensione della struttura cresce
„ Rilasciando spazio quando la dimensione
diminuisce

Come accedo

„ Le funzioni di allocazione *alloc mi


restituiscono un puntatore alla zona di
memoria allocata
„ Per accedervi, basta dereferenziare il
puntatore
„ Quindi se voglio che più funzioni accedano,
basta passare a una funzione il puntatore
come parametro

2
Tempo di vita

„ In Java, si alloca spazio utilizzando l’operatore


new
„ Quando lo spazio non serve più (e.g. ho allocato
spazio per una variabile locale, e il metodo
termina) lo spazio viene automaticamente
deallocato dal garbage collector
„ In C, sfortunatamente non abbiamo un garbage
collector
„ Anche se ne esistono…
„ Per cui, è nostra responsabilità deallocare lo
spazio che non utilizziamo più
„ Altra tipica fonte di errori!!

malloc / calloc

„ Funzioni che allocano dinamicamente blocchi di memoria

void *malloc(size_t n)
„ Restituisce un puntatore a n byte di memoria (non
inizializzata), NULL se non è possibile allocare memoria

void *calloc(size_t n, size_t size)


„ Restituisce un puntatore a uno spazio di memoria
necessario a contenere un vettore di n oggetti della misura
specificata da size
„ La memoria è inizializzata a zero

„ In entrambi i casi, ottengo un puntatore a void, quindi


occorre fare il casting al tipo opportuno
int *ip=(int *) calloc(n, sizeof(int));

3
Esempi

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


int *p=(int *) calloc(10,sizeof(int));
„ Entrambe allocano spazio per contenere 10 interi,
nel secondo caso lo spazio viene anche
inizializzato

„ Poi, posso accedere alla memoria allocata:


*(p)=1; /* assegno 1 al primo elemento */
p[2]=2; /*assegno 2 al terzo elemento */
*(p+3)=0; /*assegno 0 al quarto elemento */

realloc

void * realloc(void *ptr, size_t size)


„ Re-alloca lo spazio di memoria puntato da
ptr, allocando uno spazio di size bytes
„ Restituisce il puntatore al nuovo spazio in
memoria (che spesso non coincide con il
precedente)
„ Tutti i dati contenuti nello spazio di memoria
precedentemente allocato sono inalterati

4
Realloc: esempio
#include<stdio.h>
main()
{
int x;
int *st=(int*) malloc(10*sizeof(int)); /* alloca spazio per 10
interi */
for(x=0;x<10;x++)
{
*(st+x)=x; /* inserisce dei valori nelle 10 locazioni */
}
st=(int*)realloc(st,20*sizeof(int)); /* realloca */
for(x=10;x<19;x++)
{
*(st+x)=0; /* inserisce valori nelle restanti locazioni */
}
for(x=0;x<20;x++)
printf("%d ",*(st+x));
printf("\n");
}

free

„ Come detto, quando lo spazio non ci serve


dobbiamo deallocarlo…
free(p)
„ libera lo spazio di memoria a cui punta p

5
Tipici errori

„ Tentare di deallocare qualcosa non allocato


con malloc o calloc
int a[10];
int *p=&a;
free(p);
„ errore, p punta a uno spazio nell’area global
o stack a seconda se a è una variabile
globale o locale

Tipici errori

„ Tentare di utilizzare memoria dopo averla liberata


int c;
int *p = (int *) malloc(10 *sizeof(int));
*(p+2)=1;
free(p);
c=*(p+2); /* ERRORE, p ora punta a NULL*/
„ Se in un programma molto complesso non sono
sicuro se ho deallocato lo spazio puntato da un
puntatore, risolvo così:
if(p)
c=*(p+2);
„ Infatti, dopo la deallocazione p conterrà NULL

6
Semplice esempio
„ Leggiamo da uno stream una sequenza di n interi e poi
stampiamoli al contrario
„ Il primo intero che inseriamo è il numero di successivi
interi da considerare
„ Ad esempio se inserisco:
4
2
3
2
1
„ Stampo:
1
2
3
2

Implementazione
#include<stdio.h>
#include<stdlib.h>

main()
{
int i,x;
int *a;

if(scanf("%d",&i)==1)
{
a=(int *) malloc(i*sizeof(int));
for(x=0;x<i && scanf("%d",a+x)==1;x++);
x--;
for(;x>=0;x--)
printf("%d\n",a[x]);
free(a);
}
}

Nota: visto che il programma termina, free non e’ indispensabile perche’ la


memoria viene deallocata al termine del programma

7
Esercizio

„ Realizzare una struttura stack e le relative funzioni


push, pop e top utilizzando allocazione di memoria
dinamica
„ Prima opzione: lo spazio di memoria viene riallocato
ogni volta che si aggiunge/rimuove un elemento
„ Seconda opzione: realloco k elementi per volta. Ovvero:
„ Inizialmente alloco spazio per k elementi
„ Quando è pieno alloco spazio per altri k elementi (fino a un
massimo MAX_SIZE)
„ Non appena ci sono k locazioni libere nello stack, rialloco per
liberare spazio.

Vettori di puntatori

„ Siccome i puntatori sono variabili come


altre, è possibile creare vettori di puntatori
„ Un tipico esempio è rappresentato da un
vettore di stringhe
„ Ogni stringa è memorizzata in un’area di
memoria allocata dinamicamente
„ Il vettore contiene puntatori alle aree di
memoria in cui sono memorizzate le
stringhe

8
Vettore di puntatori a stringhe…
char *v[];
Hello

Ciao

Hola

Scambiare di posto le stringhe nel vettore significa


semplicemente scambiare i puntatori

Hello

Ciao

Hola

Esercizio
„ Realizziamo un programma che:
„ Legge linee da stdin, finché non trova EOF
„ Ordina le linee
„ Stampa le linee ordinate

„ Programma costituito (ad alto livello) da 4


funzioni:
„ readlines: legge le linee e le inserisce in una struttura
dati, restituendo il numero di linee lette
„ Allocando dinamicamente memoria per ogni nuova linea letta
„ qsort: ordina le linee nella struttura dati utilizzando
l’algoritmo del quicksort
„ writelines: stampa le linee
„ cleanup: infine… dopo aver stampato, liberiamo
spazio…
„ Al solito, non indispensabile se il programma termina

9
Soluzione: declarations…

#include <stdio.h>
#include <string.h>
#define MAXLINES 5000 /* max #lines to be sorted */
int readlines(char *lineptr[], int nlines);
void writelines(char *lineptr[], int nlines);
void qsort(char *lineptr[], int left, int right);
void swap(char *v[], int i, int j);
int partition(char *v[], int left, int right);
void cleanup(char *lineptr[], int nlines);
int getline(char *s,int maxlen);

… il main

main()
{
char *lineptr[MAXLINES];
int nlines;
if ((nlines = readlines(lineptr, MAXLINES)) >= 0) {
qsort(lineptr, 0, nlines-1);
writelines(lineptr, nlines);
cleanup(lineptr, nlines);
return 0;
} else {
printf("error: input too big to sort\n");
return 1;}
}

10
Funzione readlines
int readlines(char *lineptr[], int maxlines)
{
int len, nlines;
char *p, line[MAXLEN];
nlines = 0;
while(getLine(line,MAXLEN)!=EOF)
if(nlines >= maxlines ||
(p=(char*)malloc(strlen(line)*sizeof(char)))==NULL)
return -1;
else {
strcpy(p, line);
lineptr[nlines++] = p;
}
return nlines;
}

Discussione

„ Ogni volta che leggiamo una linea, inizialmente la


mettiamo in una variabile locale di readlines
getline(line, MAXLEN))

„ Poi, allochiamo spazio in memoria, pari alla


dimensione della linea letta
p=(char*)malloc(strlen(line)*sizeof(char)))==NULL)

„ Copiamo la linea letta in tale spazio


strcpy(p, line);

„ Inseriamo il puntatore nell’array


lineptr[nlines++] = p;

11
getline

int getline(char *s,int maxlen)


{
char c;
int i=0;
for(i=0;i<maxlen &&
(c=*s++=getchar())!=EOF &&
c!='\n';i++);
*(s-1)='\0';
if(c==EOF)
return 0;
else
return i;
}

Procediamo all’ordinamento delle stringhe…

„ Utilizziamo l’algoritmo quicksort


„ Si basa sulla partizione dell’array rispetto ad un suo
elemento scelto come “pivot”.
„ L’algoritmo di partizionamento mette a sinistra del pivot
gli elementi minori del pivot, e a destra quelli maggiori
„ Poi, si riordina ricorsivamente il lato destro e quello
sinistro
„ Complessità:
„ O(n2) nel caso peggiore
„ O(n log n) nel caso medio → è il miglior algoritmo di
ordinamento per il caso medio
„ Dimostrazione: corso di algoritmi e strutture dati
(specialistica)

12
Come funziona?
QuickSort (A,p,q)
if p < q then
i ← Partition(A,p,q)
QuickSort(A,p,i-1)
QuickSort(A,i+1,q)

„ All’inizio eseguo quicksort(A,1,12)


„ 1 e 12 sono le posizioni del primo e dell’ultimo elemento

1 2 3 4 5 6 7 8 9 10 11 12
015 1365 2301 3458 4 423 57 754 853 7 615 9

Partition: pseudo codice

Partition(A,p,q)
i ← p, j ← q, x ← A[p]
while i < j do
while A[j] > x do
j ← j-1
if i < j then
swap(A[i], A[j]), i ← i+1
while A[i] < x do
i ← i+1
if i < j then
swap(A[i], A[j]), j ← j-1
return i

13
Partition(A, 1, 12)
1 2 3 4 5 6 7 8 9 10 11 12
5 5 6 0 8 4 2 7 4 3 7 1 9
x i j

Partition(A,p,q)
i ← p, j ← q, x ← A[p]
while i < j do
while A[j] > x do
j ← j-1
if i < j then
swap(A[i], A[j]), i ← i+1
while A[i] < x do
i ← i+1
if i < j then
swap(A[i], A[j]), j ← j-1
return i

Partition(A, 1, 12)
1 2 3 4 5 6 7 8 9 10 11 12
5 5 6 0 8 4 2 7 4 3 7 1 9
x i j

Partition(A,p,q)
i ← p, j ← q, x ← A[p]
while i < j do
while A[j] > x do
j ← j-1
if i < j then
swap(A[i], A[j]), i ← i+1
while A[i] < x do
i ← i+1
if i < j then
swap(A[i], A[j]), j ← j-1
return i

14
Partition(A, 1, 12)
1 2 3 4 5 6 7 8 9 10 11 12
5 5 6 0 8 4 2 7 4 3 7 1 9
x i j

Partition(A,p,q)
i ← p, j ← q, x ← A[p]
while i < j do
while A[j] > x do
j ← j-1
if i < j then
swap(A[i], A[j]), i ← i+1
while A[i] < x do
i ← i+1
if i < j then
swap(A[i], A[j]), j ← j-1
return i

Partition(A, 1, 12)
1 2 3 4 5 6 7 8 9 10 11 12
5 1 6 0 8 4 2 7 4 3 7 5 9
x i j

Partition(A,p,q)
i ← p, j ← q, x ← A[p]
while i < j do
while A[j] > x do
j ← j-1
if i < j then
swap(A[i], A[j]), i ← i+1
while A[i] < x do
i ← i+1
if i < j then
swap(A[i], A[j]), j ← j-1
return i

15
Partition(A, 1, 12)
1 2 3 4 5 6 7 8 9 10 11 12
5 1 6 0 8 4 2 7 4 3 7 5 9
x i j

Partition(A,p,q)
i ← p, j ← q, x ← A[p]
while i < j do
while A[j] > x do
j ← j-1
if i < j then
swap(A[i], A[j]), i ← i+1
while A[i] < x do
i ← i+1
if i < j then
swap(A[i], A[j]), j ← j-1
return i

Partition(A, 1, 12)
1 2 3 4 5 6 7 8 9 10 11 12
5 1 6 0 8 4 2 7 4 3 7 5 9
x i j

Partition(A,p,q)
i ← p, j ← q, x ← A[p]
while i < j do
while A[j] > x do
j ← j-1
if i < j then
swap(A[i], A[j]), i ← i+1
while A[i] < x do
i ← i+1
if i < j then
swap(A[i], A[j]), j ← j-1
return i

16
Partition(A, 1, 12)
1 2 3 4 5 6 7 8 9 10 11 12
5 1 6 0 8 4 2 7 4 3 7 5 9
x i j

Partition(A,p,q)
i ← p, j ← q, x ← A[p]
while i < j do
while A[j] > x do
j ← j-1
if i < j then
swap(A[i], A[j]), i ← i+1
while A[i] < x do
i ← i+1
if i < j then
swap(A[i], A[j]), j ← j-1
return i

Partition(A, 1, 12)
1 2 3 4 5 6 7 8 9 10 11 12
5 1 5 0 8 4 2 7 4 3 7 6 9
x i j

Partition(A,p,q)
i ← p, j ← q, x ← A[p]
while i < j do
while A[j] > x do
j ← j-1
if i < j then
swap(A[i], A[j]), i ← i+1
while A[i] < x do
i ← i+1
if i < j then
swap(A[i], A[j]), j ← j-1
return i

17
Partition(A, 1, 12)
1 2 3 4 5 6 7 8 9 10 11 12
5 1 5 0 8 4 2 7 4 3 7 6 9
x i j

Partition(A,p,q)
i ← p, j ← q, x ← A[p]
while i < j do
while A[j] > x do
j ← j-1
if i < j then
swap(A[i], A[j]), i ← i+1
while A[i] < x do
i ← i+1
if i < j then
swap(A[i], A[j]), j ← j-1
return i

Partition(A, 1, 12)
1 2 3 4 5 6 7 8 9 10 11 12
5 1 5 0 8 4 2 7 4 3 7 6 9
x i j

Partition(A,p,q)
i ← p, j ← q, x ← A[p]
while i < j do
while A[j] > x do
j ← j-1
if i < j then
swap(A[i], A[j]), i ← i+1
while A[i] < x do
i ← i+1
if i < j then
swap(A[i], A[j]), j ← j-1
return i

18
Partition(A, 1, 12)
1 2 3 4 5 6 7 8 9 10 11 12
5 1 5 0 8 4 2 7 4 3 7 6 9
x i j

Partition(A,p,q)
i ← p, j ← q, x ← A[p]
while i < j do
while A[j] > x do
j ← j-1
if i < j then
swap(A[i], A[j]), i ← i+1
while A[i] < x do
i ← i+1
if i < j then
swap(A[i], A[j]), j ← j-1
return i

Partition(A, 1, 12)
1 2 3 4 5 6 7 8 9 10 11 12
5 1 5 0 8 4 2 7 4 3 7 6 9
x i j

Partition(A,p,q)
i ← p, j ← q, x ← A[p]
while i < j do
while A[j] > x do
j ← j-1
if i < j then
swap(A[i], A[j]), i ← i+1
while A[i] < x do
i ← i+1
if i < j then
swap(A[i], A[j]), j ← j-1
return i

19
Partition(A, 1, 12)
1 2 3 4 5 6 7 8 9 10 11 12
5 1 3 0 8 4 2 7 4 5 7 6 9
x i j

Partition(A,p,q)
i ← p, j ← q, x ← A[p]
while i < j do
while A[j] > x do
j ← j-1
if i < j then
swap(A[i], A[j]), i ← i+1
while A[i] < x do
i ← i+1
if i < j then
swap(A[i], A[j]), j ← j-1
return i

Partition(A, 1, 12)
1 2 3 4 5 6 7 8 9 10 11 12
5 1 3 0 8 4 2 7 4 5 7 6 9
x i j

Partition(A,p,q)
i ← p, j ← q, x ← A[p]
while i < j do
while A[j] > x do
j ← j-1
if i < j then
swap(A[i], A[j]), i ← i+1
while A[i] < x do
i ← i+1
if i < j then
swap(A[i], A[j]), j ← j-1
return i

20
Partition(A, 1, 12)
1 2 3 4 5 6 7 8 9 10 11 12
5 1 3 0 8 4 2 7 4 5 7 6 9
x i j

Partition(A,p,q)
i ← p, j ← q, x ← A[p]
while i < j do
while A[j] > x do
j ← j-1
if i < j then
swap(A[i], A[j]), i ← i+1
while A[i] < x do
i ← i+1
if i < j then
swap(A[i], A[j]), j ← j-1
return i

Partition(A, 1, 12)
1 2 3 4 5 6 7 8 9 10 11 12
5 1 3 0 8 4 2 7 4 5 7 6 9
x i j

Partition(A,p,q)
i ← p, j ← q, x ← A[p]
while i < j do
while A[j] > x do
j ← j-1
if i < j then
swap(A[i], A[j]), i ← i+1
while A[i] < x do
i ← i+1
if i < j then
swap(A[i], A[j]), j ← j-1
return i

21
Partition(A, 1, 12)
1 2 3 4 5 6 7 8 9 10 11 12
5 1 3 0 8 4 2 7 4 5 7 6 9
x i j

Partition(A,p,q)
i ← p, j ← q, x ← A[p]
while i < j do
while A[j] > x do
j ← j-1
if i < j then
swap(A[i], A[j]), i ← i+1
while A[i] < x do
i ← i+1
if i < j then
swap(A[i], A[j]), j ← j-1
return i

Partition(A, 1, 12)
1 2 3 4 5 6 7 8 9 10 11 12
5 1 3 0 5 4 2 7 4 8 7 6 9
x i j

Partition(A,p,q)
i ← p, j ← q, x ← A[p]
while i < j do
while A[j] > x do
j ← j-1
if i < j then
swap(A[i], A[j]), i ← i+1
while A[i] < x do
i ← i+1
if i < j then
swap(A[i], A[j]), j ← j-1
return i

22
Partition(A, 1, 12)
1 2 3 4 5 6 7 8 9 10 11 12
5 1 3 0 5 4 2 7 4 8 7 6 9
x i j

Partition(A,p,q)
i ← p, j ← q, x ← A[p]
while i < j do
while A[j] > x do
j ← j-1
if i < j then
swap(A[i], A[j]), i ← i+1
while A[i] < x do
i ← i+1
if i < j then
swap(A[i], A[j]), j ← j-1
return i

Partition(A, 1, 12)
1 2 3 4 5 6 7 8 9 10 11 12
5 1 3 0 5 4 2 7 4 8 7 6 9
x i j

Partition(A,p,q)
i ← p, j ← q, x ← A[p]
while i < j do
while A[j] > x do
j ← j-1
if i < j then
swap(A[i], A[j]), i ← i+1
while A[i] < x do
i ← i+1
if i < j then
swap(A[i], A[j]), j ← j-1
return i

23
Partition(A, 1, 12)
1 2 3 4 5 6 7 8 9 10 11 12
5 1 3 0 5 4 2 7 4 8 7 6 9
x i j

Partition(A,p,q)
i ← p, j ← q, x ← A[p]
while i < j do
while A[j] > x do
j ← j-1
if i < j then
swap(A[i], A[j]), i ← i+1
while A[i] < x do
i ← i+1
if i < j then
swap(A[i], A[j]), j ← j-1
return i

Partition(A, 1, 12)
1 2 3 4 5 6 7 8 9 10 11 12
5 1 3 0 5 4 2 7 4 8 7 6 9
x i j

Partition(A,p,q)
i ← p, j ← q, x ← A[p]
while i < j do
while A[j] > x do
j ← j-1
if i < j then
swap(A[i], A[j]), i ← i+1
while A[i] < x do
i ← i+1
if i < j then
swap(A[i], A[j]), j ← j-1
return i

24
Partition(A, 1, 12)
1 2 3 4 5 6 7 8 9 10 11 12
5 1 3 0 4 4 2 7 5 8 7 6 9
x i j

Partition(A,p,q)
i ← p, j ← q, x ← A[p]
while i < j do
while A[j] > x do
j ← j-1
if i < j then
swap(A[i], A[j]), i ← i+1
while A[i] < x do
i ← i+1
if i < j then
swap(A[i], A[j]), j ← j-1
return i

Partition(A, 1, 12)
1 2 3 4 5 6 7 8 9 10 11 12
5 1 3 0 4 4 2 7 5 8 7 6 9
x i j

Partition(A,p,q)
i ← p, j ← q, x ← A[p]
while i < j do
while A[j] > x do
j ← j-1
if i < j then
swap(A[i], A[j]), i ← i+1
while A[i] < x do
i ← i+1
if i < j then
swap(A[i], A[j]), j ← j-1
return i

25
Partition(A, 1, 12)
1 2 3 4 5 6 7 8 9 10 11 12
5 1 3 0 4 4 2 7 5 8 7 6 9
x i j

Partition(A,p,q)
i ← p, j ← q, x ← A[p]
while i < j do
while A[j] > x do
j ← j-1
if i < j then
swap(A[i], A[j]), i ← i+1
while A[i] < x do
i ← i+1
if i < j then
swap(A[i], A[j]), j ← j-1
return i

Partition(A, 1, 12)
1 2 3 4 5 6 7 8 9 10 11 12
5 1 3 0 4 4 2 7 5 8 7 6 9
x i j

Partition(A,p,q)
i ← p, j ← q, x ← A[p]
while i < j do
while A[j] > x do
j ← j-1
if i < j then
swap(A[i], A[j]), i ← i+1
while A[i] < x do
i ← i+1
if i < j then
swap(A[i], A[j]), j ← j-1
return i

26
Partition(A, 1, 12)
1 2 3 4 5 6 7 8 9 10 11 12
5 1 3 0 4 4 2 7 5 8 7 6 9
x i j

Partition(A,p,q)
i ← p, j ← q, x ← A[p]
while i < j do
while A[j] > x do
j ← j-1
if i < j then
swap(A[i], A[j]), i ← i+1
while A[i] < x do
i ← i+1
if i < j then
swap(A[i], A[j]), j ← j-1
return i

Partition(A, 1, 12)
1 2 3 4 5 6 7 8 9 10 11 12
5 1 3 0 4 4 2 7 5 8 7 6 9
x i j

Partition(A,p,q)
i ← p, j ← q, x ← A[p]
while i < j do
while A[j] > x do
j ← j-1
if i < j then
swap(A[i], A[j]), i ← i+1
while A[i] < x do
i ← i+1
if i < j then
swap(A[i], A[j]), j ← j-1
return i

27
Partition(A, 1, 12)
1 2 3 4 5 6 7 8 9 10 11 12
5 1 3 0 4 4 2 7 5 8 7 6 9
x i j

Partition(A,p,q)
i ← p, j ← q, x ← A[p]
while i < j do
while A[j] > x do
j ← j-1
if i < j then
swap(A[i], A[j]), i ← i+1
while A[i] < x do
i ← i+1
if i < j then
swap(A[i], A[j]), j ← j-1
return i

Partition(A, 1, 12)
1 2 3 4 5 6 7 8 9 10 11 12
5 1 3 0 4 4 2 5 7 8 7 6 9
x i j

Partition(A,p,q)
i ← p, j ← q, x ← A[p]
while i < j do
while A[j] > x do
j ← j-1
if i < j then
swap(A[i], A[j]), i ← i+1
while A[i] < x do
i ← i+1
if i < j then
swap(A[i], A[j]), j ← j-1
return i

28
Partition(A, 1, 12)
1 2 3 4 5 6 7 8 9 10 11 12
5 1 3 0 4 4 2 5 7 8 7 6 9
x ij

Partition(A,p,q)
i ← p, j ← q, x ← A[p]
while i < j do
while A[j] > x do
j ← j-1
if i < j then
swap(A[i], A[j]), i ← i+1
while A[i] < x do
i ← i+1
if i < j then
swap(A[i], A[j]), j ← j-1
return i

Partition(A, 1, 12)
1 2 3 4 5 6 7 8 9 10 11 12
5 1 3 0 4 4 2 5 7 8 7 6 9
x ij

Partition(A,p,q)
i ← p, j ← q, x ← A[p]
while i < j do
while A[j] > x do
j ← j-1
if i < j then
swap(A[i], A[j]), i ← i+1
while A[i] < x do
i ← i+1
if i < j then
swap(A[i], A[j]), j ← j-1
return i

29
Partition(A, 1, 12)
1 2 3 4 5 6 7 8 9 10 11 12
5 1 3 0 4 4 2 5 7 8 7 6 9
x ij

Partition(A,p,q)
i ← p, j ← q, x ← A[p] A questo punto i=7
while i < j do Tutti gli elementi prima
di tale posizione sono minori di 5
while A[j] > x do Tutti gli elementi dopo
j ← j-1 sono maggiori o uguali di 5
if i < j then
swap(A[i], A[j]), i ← i+1
while A[i] < x do
i ← i+1
if i < j then
swap(A[i], A[j]), j ← j-1
return i

qsort

void qsort(char *v[], int left, int right)


{
int i,x;
if(left<right)
{
i=partition(v, left, right);
qsort(v,left,i-1);
qsort(v,i+1,right);
}
}

30
partition
int partition(char *v[], int left, int right)
{
int i=left;
int j=right;
char x[MAXLEN];
strcpy(x,v[left]);
while(i<j)
{
while(strcmp(v[j],x)>0)j--;
if(i<j){
swap(v,i++,j);
while(strcmp(v[i],x)<0)i++;
if(i<j)
swap(v,i,j--);
}
}
return i;
}

Funzione swap su *char[]

„ Non molto differente dalla funzione swap su


interi

void swap(char *v[], int i, int j)


{
char *temp;
temp = v[i];
v[i] = v[j];
v[j] = temp;
}

31
writelines e cleanup

void writelines(char *lineptr[], int nlines)


{
int i;
for (i = 0; i < nlines; i++)
printf("%s\n", lineptr[i]);
}

void cleanup(char *lineptr[], int nlines)


{
int i;
for (i = 0; i < nlines; i++)
free(lineptr[i]);
}

Versione semplificata di qsort

void qsort(char *v[], int left, int right)


{
int i, last;
if (left >= right) /* fa nulla se l’array contiene */
return; /* meno di due elementi */
swap(v, left, (left + right)/2); /* sposta il pivot*/
last = left; /* in prima pos. */

for (i = left+1; i <= right; i++)


if (strcmp(v[i], v[left]) < 0)
swap(v, ++last, i);
swap(v, left, last); /* rimette a posto il pivot */
qsort(v, left, last-1);
qsort(v, last+1, right);
}

32
Vettori multidimensionali

„ In C è possibile dichiarare vettori


multidimensionali
int v [2][3];
/*matrice di 2 righe e 3 colonne*/

„ Accesso agli elementi:


„ int a=v[1][1] e non int a=v[1,1] !!

Rappresentazione in memoria
„ Gli elementi sono rappresentati in memoria in maniera
contigua
„ In realtà in C un vettore bi-dimensionale è trattato come un
vettore di vettori
„ v[1] punta al vettore relativo alla seconda riga del mio vettore

V[0] V[1] V[0][0] V[0][1] V[1][0] V[1][1]

int v[][2]={{2,3},{4,5}};
int *i=&v[0][0]; /*indirizzo primo elemento */
printf("%d\n",*(i+2)); /* ottengo v[1][0] ovvero 4*/
int *i2=v[1];
printf(“%d\n”,i2[1]) /* ottengo v[1][1] ovvero 5 */

33
Esempio:

„ Creiamo due funzioni


„ day_of_year: dato anno, mese, e giorno restituisce un
intero=numero di giorni dall’inizio dell’anno
„ month_day: dato il numero di giorni dall’inizio
dell’anno, e l’anno stesso, restituisce il mese e il giorno
„ Abbiamo bisogno di tener traccia dei giorni sia per
anni bisestili che per anni non bisestili
static char daytab[2][13] = {
{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
{0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};

day_of_year
int day_of_year(int year, int month, int day)
{
int i, leap;
leap = year%4 == 0 && year%100 != 0
|| year%400 == 0;
for (i = 1; i < month; i++)
day += daytab[leap][i];
return day;
}

34
month_day
void month_day(int year, int yearday,
int *pmonth, int *pday)
{
int i, leap;
leap = year%4 == 0 && year%100 != 0
|| year%400 == 0;
for (i = 1; yearday > daytab[leap][i]; i++)
yearday -= daytab[leap][i];
*pmonth = i;
*pday = yearday;
}

Passaggio di vettori multidimensionali


a funzioni
„ Una funzione che riceve un array monodimensionale era
dichiarata come
int foo(int a[]) oppure int foo(int *a);

„ Nel caso di funzioni che accettano vettori multidimensionali, è


necessario includere il numero di colonne
„ Supponiamo di avere una funzione f che riceva in input daytab
f(int daytab[2][13]){…}

„ Il numero di righe (primo indice) è irrilevente quindi:


f(int daytab[][13]){…}

„ oppure, usando I putantori:


f(int (*daytab)[13]){…}
„ Parentesi tonde necessare visto che [] ha precedenza su *

Problema: con questa notazione quando definiamo una funzione dobbiamo


conoscere la seconda dimensione dell’array multidimensionale

35
Esercizio

„ Gestire gli errori nelle funzioni day_of_year


e month_day

Differenza tra puntatori


e vettori multi-dimensionali
„ Consideriamo le definizioni
int a[10][20];
int *b[10];
„ E gli accessi a[3][4] e b[3][4]

„ Quali sono le differenze?


„ Nel primo caso sono inizializzate 200 locazioni di
memoria per contenere la matrice
„ Nel secondo caso, il compilatore riserva 10 locazioni per
puntatori *int
„ A loro volta, potrò far puntare ciascuno di essi a un
array.
„ Molto usato per realizzare array di stringhe

36
Array multidimensionale vs. puntatori
char *pname[]={“Jan”,Feb”, “Mar”, “Apr”};
char aname[][10]={“Jan”,Feb”, “Mar”, “Apr”};

pname
Jan\0
Feb\0

Mar\0

Apr\0

aname Jan\0 Feb\0 Mar\0 Apr\0


0 10 20 30

Inizializzazione di vettori di puntatori

char *month_name(int n)
{
static char *name[] = {
"Illegal month",
"January", "February", "March",
"April", "May", "June",
"July", "August", "September",
"October", "November", "December"
};
return (n < 1 || n > 12) ? name[0] :
name[n];
}
„ Name è un vettore di puntatori a stringhe costanti
„ Dimensione non precisata → Il compilatore determina la dimensione del
vettore automaticamente

37
Puntatori a puntatori

„ Dichiarati con dichiarazioni


tipo ** nome;
„ E.g.

int **a;
char **stringhe;
„ Utilissimi quando:

„ Voglio gestire matrici di dimensioni non note a


priori
„ Voglio gestire array di stringhe

Esempio: Inizializzazione dinamica matrice


bidimensionale
#include<stdio.h>
main()
{
int rows,columns;
int **matrix;
int x,y;
printf("Numero di righe:");
scanf("%d",&rows);
printf("Numero di colonne:");
scanf("%d",&columns);
matrix=(int **) malloc(rows * sizeof(int *));
for(x=0;x<rows;x++)
*(matrix+x)=(int *) malloc(columns*sizeof(int));
/* per ogni riga devo allocare spazio */

38
Esempio: Inizializzazione dinamica matrice
bidimensionale
for(x=0;x<rows;x++)
for(y=0;y<columns;y++)
{
printf("Inserisci l'elemento riga %d colonna %d:",
x,y);
scanf("%d", *(matrix+x)+y);
/* oppure scanf("%d",&matrix[x][y]); */
}
for(x=0;x<rows;x++){
for(y=0;y<columns;y++)
printf("%d ",matrix[x][y]);
/* oppure printf("%d ",*(*(matrix+x)+y)); */
printf("\n");
}
}

Passaggio di puntatori a puntatori a funzioni

„ Utili, ad esempio, quando voglio passare


„ una matrice di cui non conosco a priori le dimensioni
„ Un array di stringhe

void printMatrix(int **matrix, int rows, int columns)


{
int x,y;
for(x=0;x<rows;x++){
for(y=0;y<columns;y++)
printf("%d ",matrix[x][y]);
printf("\n");
}
}

39
Funzioni che allocano spazio

„ Se una funzione alloca una serie di oggetti interi (int *) allora devo
passargli l’indirizzo di memoria in cui è contenuto il puntatore

void readArray(int **parray, int *items)


{
int x;
printf("Numero di elementi:");
scanf("%d",items);
*parray=(int *) malloc((*items) * sizeof(int));
for(x=0;x<*items;x++){
printf("Inserisci l'elemento %d:",x);
scanf("%d", *parray+x);
}
}

Come la invoco…
„ Se ho una variabile int *arr devo passare &arr, ovvero l’indirizzo di
memoria in cui è memorizzato il puntatore

#include<stdio.h>
void readArray(int **parray, int *items);

main()
{
int *arr;
int x,items;

readArray(&arr,&items);
for(x=0;x<items;x++)
printf(" %d",arr[x]);
printf("\n");

}
„ Nota: per le funzioni che hanno come parametri puntatori a puntatori,
o array multidimensionali, è necessaria la dichiarazione anche se la
definizione è contenuta nello stesso file

40
Lettura matrice…

void readMatrix(int ***pmatrix, int *rows, int *columns)


{
int x,y;
printf("Numero di righe:");
scanf("%d",rows);
printf("Numero di colonne:");
scanf("%d",columns);
*pmatrix=(int **) malloc(*rows * sizeof(int *));
for(x=0;x<*rows;x++)
*(*pmatrix+x)=(int *) malloc(*columns*sizeof(int));
for(x=0;x<*rows;x++)
for(y=0;y<*columns;y++)
{
printf("Inserisci l'elemento riga %d colonna %d:”
,x,y);
scanf("%d", *(*pmatrix+x)+y);
}
}

Il main…

#include<stdio.h>

void readMatrix(int ***matrix, int *rows, int *columns);


void printMatrix(int **matrix, int rows, int columns);
void readArray(int **parray, int *items);

main()
{
int rows,columns;
int **matrix;
readMatrix(&matrix,&rows, &columns);
printMatrix(matrix,rows,columns);
}

41
Esercizi

„ Realizzare funzioni di algebra matriciale


„ Somma di matrici
„ Prodotto di una matrice per uno scalare
„ Prodotto di una matrice per un vettore
„ Prodotto di matrici
„ Creazione di una matrice diagonale a partire da
un vettore
„ Calcolo della matrice trasposta di una matrice
data

Argomenti su linea di comando

„ Molto spesso quando eseguiamo un programma


da shell, passiamo a tale programma una lista di
opzioni
„ echo ciao
„ ls –l *.c
„ wc –l
„ rm –rf * (Don’t try this at home!)
„ Come posso realizzare un programma C che riceva
argomenti?
„ Simile a Java (dove il main riceve un array di stringhe,
e.g. String[] args)

42
In C…

„ Non ho le stringhe, ma i puntatori a char


„ Occorre sapere la dimensione dell’array
„ In Java ho l’attributo length
„ Quindi…
„ Dovrò definire main con 2 parametri
„ Il primo di tipo intero: contiene il numero di argomenti
passati, incluso il nome del programma
„ per convenzione questo parametro è chiamato argc
„ Il secondo di tipo array di puntatori a char o puntatore a
puntatore a char: contiene i puntatori alle stringhe che
contengono gli argomenti
„ per convenzione questo parametro è chiamato argv
„ argv[0] contiene il nome del programma
„ argv[1] … argv[argc-1] gli argomenti
„ argv[argc] per convenzione contiene un puntatore NULL (0)

Dichiarazione e utilizzo argc e argv

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


„ oppure:
int main(int argc, char ** argv) {…}
„ Se il mio programma si chiama rm e lo richiamo
come
rm –rf *
argv
rm\0

argc -rf\0
3
*\0
0

43
Esempio

main(int argc, char *argv[])


{
int i;
for (i = 1; i < argc; i++)
printf("%s%s", argv[i], (i < argc-1) ? " " : "");
printf("\n");
return 0;
}

Esempio (revised)

„ Agisco sul puntatore invece che sull’indice dell’array

#include <stdio.h>

main(int argc, char *argv[])


{
while (--argc > 0)
printf("%s%s", *++argv, (argc > 1) ? " " : "");
printf("\n");
return 0;
}

44
Mini grep

„ grep è un comando Unix che identifica le


linee di un file (o di uno stream in ingresso)
che contengono (o non contengono) una
stringa
„ Beh fa molto di più (pattern matching usando
espressioni regolari)
„ grep “if(“ <prova.c
„ Stampa tutte le righe di prova.c che contengono
“if(“
„ Esercizio: realizziamo una versione
semplificata di grep

minigrep
#include <stdio.h>
#include <string.h>
#define MAXLINE 1000
int getline(char *line, int max);

main(int argc, char *argv[])


{
char line[MAXLINE];
int found = 0;

if (argc != 2)
printf("Usage: find pattern\n");
else
while (getline(line, MAXLINE) > 0)
if (strstr(line, argv[1]) != NULL) {
printf("%s", line);
found++;
}
return found;
}

45
Discussione

„ getline è la stessa funzione usata in esercizi


precedenti
„ Ricordate di includere il prototipo e di linkarne il
modulo oggetto!
„ …e non limitarsi a includere il prototipo
„ strstr è una funzione della libreria standard
stdlib.h
„ Restituisce un puntatore alla posizione del
secondo argomento (sottostringa) nel primo
(stringa)
„ NULL se la stringa non viene trovata

Comandi con opzioni (switch) -x

„ Molti comandi Unix accettano, opzioni precedute


dal “-”
„ rm –rf *
„ grep –v ciao <prova.c
„ Produce in output tutte le righe di prova.c che non
contengono ciao
„ grep –n “if(“ <prova.c
„ Produce in output le righe di prova.c che contengono il
testo “if(“ facendole precedere dal numero di riga
„ Esercizio: aggiungiamo le opzioni –v e –n al nostro
minigrep

46
Minigrep con opzioni
main(int argc, char *argv[])
{
char line[MAXLINE];
long lineno = 0;
int c, except = 0, number = 0, found = 0;

while (--argc > 0 && (*++argv)[0] == '-')


while (c = *++argv[0])
switch (c) {
case 'v':
except = 1;
break;
case 'n':
number = 1;
break;
default:
printf("minigrep: opzione illegale %c\n", c);
argc = 0;
found = -1;
break;
}

Minigrep con opzioni

if (argc != 1)
printf("Sintassi: minigrep -v -n pattern\n");
else
while (getline(line, MAXLINE) > 0) {
lineno++;
if ((strstr(line, *argv) != NULL) != except) {
if (number)
printf("%ld:", lineno);
printf("%s", line);
found++;
}
}
return found;
}

47
Esercizio

„ Aggiungete a minigrep:
„ –i
„ Rende il matching case-insensitive
„ -c
„ Non produce in output le righe matchate, ma le
conta e al termine stampa il numero di match
effettuati

Altre utilità Unix

„ head –n <file.txt
„ Stampa le prime n righe di file.txt
„ Se ometto –n stampa le prime 10 righe
„ tail –n <file.txt
„ Stampa le ultime n righe di file.txt
„ wc <fiile.txt
„ Stampa il numero di caratteri, righe e parole
„ wc –l <fiile.txt
„ Solo il numero di righe
„ wc –c <fiile.txt
„ Solo il numero di caratteri
„ wc –w <fiile.txt
„ Solo il numero di parole
„ .. Chiaramente posso combinare le varie opzioni
„ Esercizi: … ovvio, no?

48
Curiosità … combinare comandi con pipe

„ Come stiamo vedendo, la maggior parte dei


comandi Unix sono filtri
„ Leggono da STDIN
„ Ricevono in ingresso eventuali opzioni
„ Producono output su STDOUT
„ Unix mi consente di utilizzare il meccanismo
delle pipe “|” per combinare comandi
„ Se scrivo:
comando1 | comando2
L’output di comando1 diventa l’input di
comando 2

And now…

„ Impariamo ad usare le pipe da shell…


„ Vedremo in seguito come il meccanismo di
comunicazione delle pipe è realizzato
mediante chiamate di sistema

49
Esempi

„ cat prova.c | grep “if” | wc –l


„ Conta le linee di prova.c che contengono “if”
„ cat lista.csv | grep “Gianni” | cut –d, -f3
„ Prende un file in formato comma separated value

Cliente 1, Gianni, 1000


Cliente 2, Pippo, 2500
Cliente 3, Marco, 3500
„ Prima, filtra le righe che contengono “Gianni”. Poi,
Per ogni riga, estrae il terzo campo considerando
“,” come separatore (vedere comando Unix cut)
„ L’output sarà:
1000

Esercizio: comando minicut

„ Realizzate una versione semplificata di cut


(minicut)
„ Prima semplificazione: nessuno switch,
assumiamo che minicut accetti come primo
parametro il separatore e come secondo
l’indice di campo
„ ./minicut , 3
„ Seconda versione: provare a implementare
la sintassi corretta di cut
„ ./minicut –d, -f3

50
Binding dinamico
„ Linguaggi come Java consentono il binding dinamico
„ Possibilità di decidere a runtime quale metodo invocare
„ Supponiamo di avere 2 classi Java, A e B
„ B estende A
„ entrambe hanno un metodo m()
„ Consideriamo il seguente frammento di codice:

A a1=null;
if(condizione)
a1=new A();
else
a1=new B();
a1.m();

„ Quale metodo verrà invocato nello statement a1.m()?

Binding dinamico in C: puntatori a funzioni

„ Come abbiamo visto, lo spazio di memoria


contiene una zona (text) in cui è contenuto
il codice del programma in linguaggio
macchina
„ Anche se una funzione non è una variabile,
essa ha un indirizzo, quindi è possibile
definire puntatori a funzioni
„ Tali puntatori possono essere assegnati,
inseriti in array, passati a funzioni, restituiti
da funzioni, etc.

51
Esempio giocattolo…
#include <stdio.h>
int sum(int a, int b);
int product(int a, int b);
main()
{
int (*fun)(int, int); Dichiaro una variabile di tipo
puntatore a funzione di nome
char op; fun, che ritorna un int e accetta
int num1,num2; come parametri 2 int
scanf("%d",&num1);
scanf("%d",&num2);
scanf("%c",&op);

Attenzione! Devo usare le parentesi attorno a


*fun in int (*fun) (int, int)
Se scrivevo int * fun(int, int) cosa dichiaravo?

Esempio giocattolo…
„ Assegno a fun il puntatore alla funzione product
„ (int (*) (int, int)) fa il cast del puntatore al tipo
della funzione
Notare le parentesi attorno a *
„ N.B. per prendere l’indirizzo di product basta il
switch(op) nome product e non &product
{ „ come per gli array
case '*':
fun=(int (*)(int,int)) product;
break;
case '+':
fun=(int (*)(int,int)) sum;
break;
default:
printf("Operatore non supportato\n");
}
printf("Risultato: %d\n",
(* fun)(num1,num2)); Invocazione dereferenziando il puntatore
} a funzione

52
Le funzioni….

int sum(int a, int b)


{
return a+b;
}

int product(int a, int b)


{
return a*b;
}

Esercizio: sorting generico

„ Modifichiamo il nostro programma di


ordinamento di linee rendendolo più simile
al filtro sort di Unix
„ cat file1.txt | sort
„ Ordina le linee alfabeticamente
„ cat file1.txt | sort –n
„ Ordina le linee considerando il numero intero
che trova nella prima posizione della stringa
„ E.g. 1111 è “alfabeticamente” minore di 22, ma
numericamente maggiore di 22

53
Dichiarazione di funzioni…

#include <stdio.h>
#include <string.h>

#define MAXLINES 5000 /* max #lines to be sorted */


char *lineptr[MAXLINES]; /* pointers to text lines */

int readlines(char *lineptr[], int nlines);


void writelines(char *lineptr[], int nlines);

void qsort(void *lineptr[], int left, int right,


int (*comp)(void *, void *));
int numcmp(char *, char *);

Nuova quicksort

void qsort(void *lineptr[], int left,


int right,
int (*comp)(void *, void *));
„ I primi tre parametri sono gli stessi
„ Il quarto parametro:
„ È un puntatore a funzione, che sarà riferito con il
nome comp
„ La funzione restituisce un intero
„ La funzione accetta 2 parametri void *

54
Il main…

main(int argc, char *argv[])


{
int nlines; /* number of input lines read */
int numeric = 0; /* 1 if numeric sort */

if (argc > 1 && strcmp(argv[1], "-n") == 0)


numeric = 1;
if ((nlines = readlines(lineptr, MAXLINES)) >= 0) {
qsort((void**) lineptr, 0, nlines-1,
(int (*)(void*,void*))(numeric ? numcmp : strcmp));
writelines(lineptr, nlines);
return 0;
} else {
printf("input too big to sort\n");
return 1;
}
}

Chiamata a qsort

qsort((void**) lineptr, 0, nlines-1,


(int (*)(void*,void*))
(numeric ? numcmp : strcmp)

„ Se numeric!=0 la chiamata sarà


qsort((void **)lineptr, 0, nlines-1, (int (*) (void*, void*)) numcmp
„ Altrimenti sarà
qsort((void **)lineptr, 0, nlines-1, (int (*) (void*, void*)) strcmp
„ Come notiamo quando passo “l’indirizzo” di una funzione indico
semplicemente il nome (strcmp o numcmp) e non &strcmp o
&numcmp
„ Come per gli array
„ (int (*) (void*, void*)) effettua un casting del puntatore a funzione, in
maniera conforme al quarto parametro nel prototipo di qsort

55
Funzione qsort

void qsort(void *v[], int left, int right,


int (*comp)(void *, void *))
{
int i, last;
void swap(void *v[], int, int); /* Dichiaro swap perché
contenuta in un altro file (avrei potuto farlo anche
globamente invece che all’interno di qsort */

if (left >= right)


return;
swap(v, left, (left + right)/2);
last = left;
for (i = left+1; i <= right; i++)
if ((*comp)(v[i], v[left]) < 0)
swap(v, ++last, i);
swap(v, left, last);
qsort(v, left, last-1, comp);
qsort(v, last+1, right, comp);
}

Chiamata funzione tramite puntatore

(*comp)(v[i], v[left])
„ Dereferenzia il puntatore a funzione comp e
chiama la funzione passata come parametro
„ strcmp o numcmp
„ passandole come parametro i puntatori a void v[i] e
v[left]

56
numcmp

„ Confronta 2 stringhe numericamente usando atof

int numcmp(char *s1, char *s2)


{
double v1, v2;
v1 = atof(s1);
v2 = atof(s2);
if (v1 < v2)
return -1;
else if (v1 > v2)
return 1;
else
return 0;
}

swap su void *

„ Simile alle altre…

void swap(void *v[], int i, int j;)


{
void *temp;
temp = v[i];
v[i] = v[j];
v[j] = temp;
}

57
Esercizio

„ Aggiungere l’opzione –r per consentire


l’ordinamento decrescente, anche in
presenza di -n

58

Das könnte Ihnen auch gefallen