56
Esercitazione 10 Strutture ed Enumerazioni Allocazione dinamica di memoria

Strutture ed Enumerazioni Allocazione dinamica di memoriaprog1/Esercizi_in_aula/Esercizi10.pdf · Esercitazione 10 Strutture ed Enumerazioni ... Operazioni su strutture Si possono

Embed Size (px)

Citation preview

Esercitazione 10

Strutture ed Enumerazioni

Allocazione dinamica di memoria

ESERCIZIOScrivere una funzione che, dato un array di interi bidimensionale di

dimensione n × n, calcoli e stampi la somma degli elementi chestanno sotto, sopra e sulla diagonale principale.

somma degli elementi sotto la diagonale: -3+2+30+11+78+18

somma degli elementi sopra la diagonale: 4+0+3+0+57+23

somma degli elementi sulla diagonale: 0+64+2+84

0 4 0 3

-3 64 0 57

2 30 2 23

11 78 18 84

0

1

2

3

0 1 2 3

Funzione somma

*/ la funzione somma utilizza i puntatori per restituire i tre valori richiesti. */

void somma (int a [ MAX_N][MAX_N], int n,

int *somma_sotto, int *somma_sopra, int *somma_diag) {

int i, j;

*/ inizializzazione delle variabili per le somme */

*somma_sotto = 0;*somma_sopra = 0;

*somma_diag = 0;

*/ somma degli elementi sotto la diagonale principale */

for (i=1; i<n; i++)

for (j =0; j<i ; j++)*somma_sotto +=a[i][j];

*/ somma degli elementi sopra la diagonale principale */

for (i=0; i<n-1; i++)for (j =i+1; j<n ; j++)

*somma_sopra +=a[i][j];

*/ somma degli elementi sulla diagonale principale */

for (i=0; i < n; i++)

*somma_diag +=a[i][i];

return ;

}

TypedefLa parola chiave typedef viene usata per assegnare un alias a un qualsiasi tipo, fondamentale o derivato.

Esempio

typedef char * Stringa;Stringa s1, s2;

Il tipo char *, cioè il tipo puntatore a carattere, viene ribattezzato Stringa. Quindi si definiscono due variabili, s1 e s2 di tipo Stringa.

Typedeftypedef int Interi; // Il tipo int viene ribattezzato Interi

typedef Stringa Lista_spesa[30];Lista_spesa tab;tab[0]=“pane”; tab[1]=“latte”;

Lista_spesa è il tipo array di 30 puntatori a carattere.

La variabile tab di tipo Lista_spesa è un array di puntatori a carattere, di cui inizializzo i prime due.

Tipi derivati

A partire dai tipi fondamentali (int, float, double, char) è possibilecostruire nuovi tipi, detti tipi derivati.

Gli array e i puntatori sono esempi di tipi derivati, nel senso che peressere specificati hanno bisogno di riferirsi a un tipo base.

Altri esempi di tipi derivati sono

i tipi structure (strutture)i tipi enumerazione

Tipi structureEsempio Il seguente tipo struttura rappresenta il concetto di data.

struct Data {int giorno;char *mese;int anno;} ; /* dichiarazione del tipo data */

Questa dichiarazione introduce un nuovo tipo, il tipo Data. È ora possibile dichiarare variabili di tipo struct Data nel seguente modo:

struct Data ieri, oggi; /* ieri e oggi sono variabili di tipo struct Data */

La variabile oggi è una variabile strutturata composta di tre campi:due di tipo int - giorno e anno - e una di tipo stringa - mese.

Tipi structureLa sintassi generale per la definizione di un tipo struttura è:

struct nome_struttura {tipo_campo1 nome_campo1;tipo_campo2 nome_campo2;...tipo_campoN nome_campoN;} ;

Gli elementi di una struttura sono detti campi; essi sono identificati da un nome e da un tipo che può essere sia fondamentale che derivato. La struttura così definita è un nuovo tipo a tutti gli effetti.Si possono definire variabili di tipo struct nome_struttura come segue:

struct nome_struttura nome_variabile;

typedef e tipi structureEsempio

typedef struct {int giorno;char *mese;int anno;} Data ; /* dichiarazione del tipo Data */

Questa dichiarazione introduce un nuovo nome per il tipo struttura.È ora possibile dichiarare variabili di tipo Data nel seguente modo:

Data ieri, oggi; /* ieri e oggi sono variabili di tipo Data */

EsempioLa seguente definizione introduce la struttura Automobile.Vengono dichiarate quindi due variabili di tipo Automobile, a1 e a2.

typedef struct {char *marca;char *modello;int numero_vendute;

} Automobile;

Automobile a1, a2;

Tipi structure

Per accedere ai campi di una variabile di tipo struttura si fa usodell’operatore punto (.)

ESEMPIOSi consideri la struttura Data e le variabili ieri e oggi di tipo Data.Possiamo inizializzare le variabili ieri e oggi nel seguente modo:

oggi.giorno = 30;oggi.mese = “Novembre”;oggi.anno = 2007;ieri.anno = oggi.anno;

printf(“%d %s %d”, oggi.giorno, oggi.mese, oggi.anno);

Operazioni su struttureSi possono assegnare variabili di tipo struttura a variabili dello stesso tipo struttura.

Data d1, d2;...d1 = d2;

Nota: questo permette di assegnare interi vettori.typedef struct {

int a[12];char b; } Prova x, y;

...x = y;

Non è possibile effettuare il confronto tra due variabili di tipo struttura.Data d1, d2;if (d1 == d2) ... Errore!

Esempio/* definizione della struttura Automobile */

#define <stdio.h>

typedef struct {char *marca;char *modello;int numero_vendute;

} Automobile;

int main ( ) {

Automobile a1, a2; (continua)

a1.marca = “FERRARI”;a1.modello = “F40”;a1.numero_vendute = 200;

a2.marca = “OPEL”;a2.modello = “ASTRA”;a2.numero_vendute = 2000;

printf (“marca auto = %s\n”, a1.marca);printf (“modello auto = %s\n”, a1.modello);printf (“vendute = %d\n”, a1.numero_vendute);

printf (“marca auto = %s\n”, a2.marca);printf (“modello auto = %s\n”, a2.modello);printf (“vendute = %d\n”, a2.numero_vendute);

}

ESERCIZIOStabilire se il seguente codice e’ sintatticamente corretto e, in tal caso,

cosa produce a video.

typedef struct {int a;

} S1;

typedef struct {int a;

} S2;

S1 bob, ric;S2 gio;

bob.a = 30;ric = bob;gio = bob;printf (“%d, %d, %d \n”, bob.a, ric.a, gio.a);

SOLUZIONE

typedef struct {int a;

} S1;

typedf struct {int a;

} S2;

S1 bob, ric;S2 gio;

bob.a = 30;ric = bob;gio = bob; /* errore: non si può assegnare una variabile di tipo

S1 a una di tipo S2 */printf (“%d, %d, %d \n”, bob.a, ric.a, gio.a);

ESERCIZIOStabilire se il seguente codice e’ sintatticamente corretto e, in tal caso,

cosa produce a video.

typedef struct {int giorno;char *mese;int anno;

} Data;

Data *pd, compleanno;pd = &compleanno;*pd.giorno = 30;*pd.mese = “Novembre”;*pd.anno = 2007;

printf (“Il mio compleanno e’ il: %d, %s, %d \n”, compleanno.giorno,compleanno.mese, compleanno.anno);

SOLUZIONE

typedf struct {int giorno;char *mese;int anno;

} Data;

Data *pd, compleanno;pd = &compleanno;*pd.giorno = 30; /* errore: poiché l’operatore “.” ha priorità*pd.mese = “Novembre”; maggiore rispetto all’operatore “*” bisogna*pd.anno = 2007; usare le parentesi: (*pd).giorno ... ,

(*pd).mese..., (*pd).anno ... */

printf (“Il mio compleanno e’ il: %d, %s, %d \n”, compleanno.giorno,compleanno.mese, compleanno.anno);

ESERCIZIOStabilire se il seguente codice e’ sintatticamente corretto e, in tal caso, cosa

produce a video.

typedef struct {int giorno;char *mese;int anno;

} Data;

Data compleanno = { 30, “Novembre”, 2007 };

Data *pd;pd = &compleanno;pd -> giorno = compleanno -> giorno;pd -> mese = compleanno -> mese;pd -> anno = compleanno -> anno;

printf (“Il mio compleanno : %d, %s, %d \n”, pd.giorno, pd.mese, pd.anno);

SOLUZIONE

typedef struct {int giorno;char *mese;int anno;

} Data;

Data compleanno = { 30, “Novembre”, 2007 };

Data *pd;pd = &compleanno;pd -> giorno = compleanno -> giorno; / / errore: compleanno.giornopd -> mese = compleanno -> mese; / / errore: compleanno.mesepd -> anno = compleanno -> anno; / / errore: compleanno.anno

printf (“Il mio compleanno : %d, %s, %d \n”, pd.giorno, pd.mese, pd.anno); / / errore: pd->giorno, pd->mese, pd->anno

Esercizio

Scrivere una funzione che, dato un puntatore a una variabile di tipoData, restituisce

il numero del mese relativo alla data puntata dal puntatorein caso di errore, restituisce 0.

SOLUZIONE

int numero_mese (Data *pd){

if (! strcmp(pd -> mese , “Gennaio”)) /* l’operatore “ -> ” permette return (1); di accedere direttamente ai campi di una

variabile strutturata puntata da un puntatore */if (! strcmp (pd -> mese , “Febbraio”))

return (2);

....if (! strcmp (pd -> mese , “Dicembre”))

return (12);

return (0); }

Strutture di Strutture

In C è possibile definire strutture di strutture

Regola: le strutture che compaiono nei campi di una struttura devono essere state definite prima della struttura che le contiene.

EsempioDefinire una struttura utile per la gestione di un autosalone.

typedef struct {int giorno;char *mese;int anno;

} Data;

typedef struct {char *marca;char *modello;int numero_vendute;Data data_prima_produzione;

} Automobile;

Automobile salone[100];

EsempioDefinire una struttura utile per la gestione di un conto corrente.

typedef struct {int giorno;char *mese;int anno;

} Data;

typedef struct {int numero_conto;char nome[80];float bilancio;Data ultimo_movimento;

} Conto_corrente;

Tipi enumerazioneSpesso si ha l’esigenza di definire un insieme finito di valori alternativi da associare ad un oggetto. Ad esempio i 4 semi delle carte da poker sono “cuori”, “fiori”, “quadri”, “picche”.In genere si memorizzano questi valori associando un numero costante univoco a ciascuno. Perciò si potrebbe scrivere

const int cuori = 1;const int fiori = 2;const int quadri = 3;const int picche = 4;

Questa tecnica ha dei punti deboli. Il principale è che non c’e’ mododi restringere l’insieme dei valori assegnabili ad una variabile ai soli“cuori”, “fiori”, “quadri”, “picche”.

Tipi enumerazioneI tipi enumerazione forniscono un metodo alternativo non solo per definire ma anche per raggruppare insiemi di costanti.

Ad esempio:

enum Seme {cuori = 1, fiori, quadri, picche} ;

Così ho dichiarato un nuovo tipo enum Seme, che è un tipo enumerazione.I valori che appartengono al tipo enum Seme sono solo quattro: cuori, fiori, quadri, picche, e corrispondono agli interi 1,2,3 e 4.

Nota: per default, al primo enumeratore è assegnato il valore 0 e ai successivi i valori 1,2,ecc...Noi abbiamo assegnato a cuori il valore 1. A fiori è assegnato automaticamente il valore 2, a quadri il valore 3 e a picche il valore 4.

typedef e tipi enumerazioneEsempio:

typedef enum Seme {cuori = 1, fiori, quadri, picche} Seme ;Seme p, s;

Così ho rinominato il tipo enum Seme con il nuovo nome Seme.

p ed s sono due variabili di tipo Seme.

Tipi enumerazioneOggetti di tipo enumerazione possono essere definiti, prendere parte a espressioni ed essere passati come argomenti a funzioni.

Un oggetto di tipo enumerazione può essere inizializzato con - oppure è possibile assegnargli - solo un oggetto dello stesso tipo enumerazione.

Esempio

typedef enum Seme {cuori = 1, fiori, quadri, picche} Seme;

Seme s = quadri; /* corretto, corrisponde a s = 3 */Seme p = 4; /* corretto (ma “brutto”), equivale a p = picche */s = p; /* corretto */

Tipi enumerazioneNon è possibile stampare i nomi effettivi degli enumeratori.

Esempio

typedef enum Seme {cuori = 1, fiori, quadri, picche} Seme;

Seme s = quadri;

printf(“s = %d”, s); /* stampa s = 3 */

EsercizioDire cosa stampa il seguente frammento di codice.

typedef enum Seme {cuori = 1, fiori, quadri, picche} Seme;

typed struct {Seme seme;int numero;

} Carta;

Carta carta1, carta2;carta1.seme = cuori;carta1.numero = 1;carta2.seme = quadri;carta2.numero = 10;carta1 = carta2;printf(“seme carta 1 = %d\n”, carta1.seme);printf(“numero carta 1 = %d\n”, carta1.numero);

Soluzione

typedef enum Seme {cuori = 1, fiori, quadri, picche} Seme;

typedef struct {Seme seme;int numero;

} Carta;

Carta carta1, carta2;carta1.seme = cuori;carta1.numero = 1;carta2.seme = quadri;carta2.numero = 10;carta1 = carta2;printf(“seme carta 1 = %d\n”, carta1.seme);printf(“numero carta 1 = %d\n”, carta1.numero);

stampa

seme carta 1 = 3numero carta 1 = 10

Consideriamo il seguente tipo di dato:

typedef struct Student{char cognome[15];char nome[15]int voto;

} Studente;

Vogliamo costruire delle procedure che ci permettano di lavorare con tabelle di studenti: riempirle (con dati immessi dall’utente), ordinarle in ordine alfabetico, stamparle, ecc.

Esercizio

void leggi(Studente classe[], int nstudenti){int i;for ( i = 0 ; i < nstudenti ; i++ ){

printf("\ncognome = ");scanf("%s", classe[i].cognome);printf("nome = ");scanf("%s", classe[i].nome);printf("voto = ");scanf("%d", &classe[i].voto) ;

} }void scrivi(Studente classe[], int nstudenti){

int i;for ( i = 0 ; i < nstudenti ; i++ ){

printf("\n cognome = %s“ classe[i].cognome);printf("\n nome = %s", classe[i].nome);printf("\n voto = %d\n", classe[i].voto);

} }

Viene passato il riferimento

all’array(nessuna copia!)

Ordinamento per inserzione

2 4 6 9 5 3 8 1 7

Parte già ordinata Elemento da considerare

adesso

0

… passi successivi0 1 ii-1

Ordinamento per inserzione

2 4 6 9 3 8 1 7

Elemento da considerare

adesso

0

… passi successivi0 1 ii-1

5temp

Ordinamento per inserzione

2 4 6 9 3 8 1 7 0

… passi successivi0 1 ii-1

5temp

Ordinamento per inserzione

2 4 6 9 3 8 1 7 0

… passi successivi0 1 ii-1

5temp

Ordinamento per inserzione

2 4 6 9 3 8 1 7 0

… passi successivi0 1 ii-1

5

temp

Parte già ordinataProssimo elemento

da considerare

Torniamo all’esercizio...

La tecnica di ordinamento per inserzione si applica ad array di qualsiasi tipo.

In questo caso particolare, due elementi stud1 e stud2 dell’arraysono ordinati se

stud1.cognome < stud2.cognomeoppure

stud1.cognome == stud2.cognome e stud1.nome < stud2.nome

rispetto all’ordine lessicografico (quello dell’elenco telefonico!)

Ordinamentoint maggiore(Studente p, Studente q){

return (strcmp(p.cognome, q.cognome)>0 ||(strcmp(p.cognome, q.cognome)==0

&& strcmp(p.nome, q.nome)>0)); }

void inserisci( Studente a[], int i ){int h;Studente temp = a[i] ;

for ( h = i ; h > 0 && maggiore(a[h-1],temp); h-- ){a[h] = a[h-1];

}a[h] = temp ;

}

void ordina(Studente a[], int nstudenti){int i;for (i=1; i<nstudenti; i++)

inserisci(a,i);}

Fine esercizio...

A questo punto non rimane che scrivere il programma principale...

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

#define NUM_STUDENTI 300. . .int main(void){

Studente classe[NUM_STUDENTI];int nstud = 120;

leggi(classe, nstud);scrivi(classe, nstud);ordina(classe, nstud);scrivi(classe,nstud);

}

EsercizioDichiarare un tipo enumerazione Colore che rappresenta i due colori delle pedine degli scacchi (bianco e nero), ed un tipo enumerazione Personaggio che rappresenta i diversi tipi di pedina (re, regina, alfiere, cavallo, torre e pedone).

typedef enum Colore { bianco, nero } Colore;

typedef enum Personaggio { re, regina, alfiere, cavallotorre, pedone } Personaggio;

EsercizioDichiarare un tipo record Pedina, che rappresenta una pedina degli scacchi, con due campi: colore e personaggio, di tipo Colore e Personaggio, respettivamente.

typedef struct {

Colore colore;Personaggio personaggio;

} Pedina;

EsercizioDichiarare un tipo record Casella, che rappresenta una casella della scacchiera, ed è un record di due campi: colore (il colore della casella, di tipo Colore) e pezzo, un riferimento ad una variabile di tipo Pedina (il riferimento sarà NULL se la casella non contiene nessuna pedina,altrimenti conterrà l’indirizzo di una variabile di tipo Pedina).

typedef struct {

Colore colore;Pedina *pezzo;

} Casella;

EsercizioDichiarare il tipo Scacchiera, i cui elementi sono tabelle bidimensionali di dimensione 8 per 8 aventi come tipo base il tipo Casella. Un valore di tipo Scacchiera rappresenta una possibile configurazione della scacchiera.

typedef Casella Scacchiera [8][8];

Scacchiera s;

EsercizioUtilizzando i tipi definiti precedentemente, inizializzare una variabile s di tipo Scacchiera in modo che rappresenti una scacchiera in cui tutte le caselle sono nere e sia presente solo il re bianco in posizione (0,0).

Scacchiera s;int i, j;Pedina *king;

for (i=0; i<8; i++)for (j=0; j<8; j++) {

s[ i ][ j ].colore = nero;s[ i ][ j ].pezzo = NULL; }

king = (Pedina*) malloc(sizeof(Pedina));king ->colore = bianco;king ->personaggio = re;s[0][0].pezzo = king;

EsercizioUtilizzando il modello ambiente-memoria, descrivere graficamente la situazione al termine dei comandi necessari a realizzare quanto richiesto dall’esercizio precedente.

Scacchiera s heapstackmemoriaambiente

NULL

biancore

nero

nero

nero

nero

NULL

NULL

....

•Pedina *king

Esercizio

Utilizzando i tipi definiti negli esercizi precedenti, scrivere la definizione di una funzione che,

prendendo come parametro attuale un valore s di tipo Scacchieraverifica se la torre nera è presente nella scacchiera s.

La funzione restituisce 1 se la torre nera è presente, altrimenti restituisce 0.

Soluzione

int cercaTorreNera (Scacchiera s) {int i, j;for (i=0; i<8; i++)

for (j=0; j<8; j++) if ((s[i][j].pezzo != NULL) &&

((s[i][j].pezzo)->colore == nero) &&((s[i][j].pezzo)->personaggio == torre) )

return 1;

return 0;}

Allocazione dinamicatypedef struct {

char titolo[30];char autore[15];

} Libro;

Libro *lib;

lib = (Libro *) malloc (sizeof(Libro));

printf(“Inserisci titolo senza spazi: “);scanf(“%s”, lib -> titolo);

printf(“Inserisci autore: “);scanf(“%s”, lib -> autore);

Librotitolo

autore

Allocazione dinamicatypedef struct {

char titolo[30];char autore[15];

} Libro;

/* altro modo di inizializzare */Libro *lib;

lib = (Libro *) malloc (sizeof(Libro));

strcpy( lib -> titolo, “La divina commedia”);strcpy( lib -> autore, “Dante”);

Librotitolo

autore

NOTA:Se inserisco “Harry Potter e l’ordine della fenice”, si esce dai limiti !!!

E se inserisco “Iliade” spreco spazio

Allocazione dinamicatypedef struct {

char *titolo;char *autore;

} Libro;

Libro *lib;

lib = (Libro *) malloc (sizeof(Libro));

/* così ho allocato spazio solo per due puntatori */

/* prima di inserire le stringhe per titolo e autore

occorre allocare lo spazio necessario a

contenerle */

Libro

titolo

autore

Allocazione dinamicatypedef struct {

char *titolo;char *autore;

} Libro;

Libro *lib;

lib = (Libro *) malloc (sizeof(Libro));

lib->titolo =(char *) malloc (sizeof(char)*40);

lib->autore =(char *) malloc (sizeof(char)*15);

strcpy( lib->titolo, “Harry Potter e l’ordine della fenice”);strcpy( lib->autore, “J.K. Rowling”);

Libro

titolo

autore

Allocazione dinamica/* così posso usare la memoria in modo efficiente: */

Libro *lib2;

lib2 = (Libro *) malloc (sizeof(Libro));

lib2->titolo =(char *) malloc (sizeof(char)*7);

lib2->autore =(char *) malloc (sizeof(char)*6);

strcpy( lib2->titolo, “Iliade”);strcpy( lib2->autore, “Omero”);

Librotitolo

autore

‘I’ ‘l’ ‘i’ ‘a’ ‘d’ ‘e’ ‘\0’

‘O’ ‘m’ ‘e’ ‘r’ ‘o’ ‘\0’