le strutture dati

annuncio pubblicitario
1
ISTITUTO DI ISTRUZIONE SUPERIORE “ANGIOJ”
LE STRUTTURE DATI
PARTE 2: RECORD
Prof. G. Ciaschetti
Abbiamo visto nella prima dispensa sulle strutture dati che una struttura dati è una collezione di
dati in memoria ai quali è possibile dare un unico nome. Abbiamo studiato finora gli array,
strutture dati omogenee e lineari, dove per omogenee si intende il fatto che i dati che esse
contengono sono tutti dello stesso tipo (o tutti interi, o tutte stringhe, o tutti reali, ecc.), mentre per
lineari si intende che i dati della collezione sono memorizzati in modo contiguo in RAM, uno dietro
l’altro.
Oltre agli array, esistono strutture dati non omogenee: i record. Un record (registrazione, in
italiano) è la struttura base per la creazione degli archivi, ed è una struttura dati che serve per
memorizzare tutti i dati relativi a un determinato individuo dell’archivio. Degli archivi ci
occuperemo nella prossima unità didattica. Facciamo, per ora, qualche esempio per capire meglio:
In una biblioteca, gli individui sono i diversi libri e avremo, pertanto, un diverso record per ogni
diverso libro, per poter memorizzare, ad esempio, il suo titolo, l’autore, l’anno di pubblicazione, e
la casa editrice.
record libro
Nell’anagrafe del comune di Carbonia, gli individui sono i cittadini e ogni record memorizzerà, per
ogni cittadino, ad esempio, il nome, il cognome, la data di nascita, il domicilio.
record cittadino
In un canile municipale, gli individui sono i diversi cani ospitati, e per ognuno di essi si avrà un
record che memorizzerà il nome, la razza, l’età.
record cane
2
I dati che compongono un record si chiamano campi, e ognuno di essi ha un nome e un tipo.
Nell’esempio precedente della biblioteca, il record libro ha:
- il campo titolo di tipo stringa,
- il campo autore di tipo stringa,
- il campo anno di tipo intero,
- il campo editore di tipo stringa.
Nell’esempio del canile, il record cane ha:
- il campo nome di tipo stringa,
- il campo razza di tipo stringa,
- il campo eta di tipo intero.
Quanti campi occorre mettere per ogni record? Vedremo, nella prossima unità didattica, quando
parleremo di archivi, che occorre fare un giusto compromesso tra il numero di informazioni che
vogliamo memorizzare per ogni individuo, e lo spazio occupato in memoria. Come criterio
generale, per il momento, possiamo adottare il seguente: rappresentare solo le informazioni utili ai
nostri scopi. Ad esempio, nella biblioteca, è inutile memorizzare per ogni libro il numero delle
pagine, in quanto questo dato non serve in nessuna delle operazioni che si dovranno fare (prestito,
restituzione, ecc.), e comporterà solo uno spreco di memoria.
In linguaggio C, i record si chiamano strutture (structure, in inglese), e devono essere definiti
prima di poterli utilizzare.
Definizione di un record in C
Per definire un record, in linguaggio C, occorre scrivere:
struct nome
{
tipo1 campo1;
tipo2 campo2;
…
tipoN campoN;
};
La parola chiave struct del linguaggio C informa il compilatore che si sta definendo un nuovo tipo
di dati, che è un record, che si chiama nome e che contiene i campi campo1, campo2, …, campoN,
rispettivamente di tipo tipo1, tipo2, …, tipoN.
Il nuovo tipo di dato così definito sarà il tipo struct nome.
ATTENZIONE: una definizione di record non è una dichiarazione di variabile, cioè non riserva
spazio in memoria! Piuttosto, è una definizione di tipo di dato. Abbiamo, cioè, oltre ai tipi di dato
predefiniti del C (int, float, char, ecc.), un nuovo tipo di dato che si chiama struct nome. Questo
significa che per poter effettivamente andare a memorizzare dati in un record, occorre avere spazio
in memoria, e cioè dichiarare variabili del nuovo tipo: solo la dichiarazione di variabili riserva
spazio in memoria!
Riprendiamo i nostri esempi, e andiamo a definire i record libro, cittadino e cane.
3
struct libro
{
char
char
int
char
};
struct cittadino
{
char
char
int
char
};
struct cane
{
char
char
int
};
titolo[50];
autore[20];
anno;
editore[20];
nome[20];
cognome[20];
anno_di_nascita;
domicilio[50];
nome[20];
razza[20];
eta;
Nota: per semplicità di esposizione, nel campo data di nascita del record cittadino abbiamo scelto
di rappresentare solo l’anno, perché l’informazione completa è in effetti composta da tre dati: anno,
mese, giorno. Per fare le cose in modo più preciso, avremmo potuto utilizzare un campo di tipo
array di tre interi, oppure definire tre diversi campi.
Si presti attenzione a come sono stati scelti i nomi per i record nei precedenti esempi. Un record
memorizza i dati di un individuo di un archivio, quindi è opportuno scegliere il nome in modo da
ricordarci che si tratta di una registrazione relativa a un solo individuo. Sarebbe stato sbagliato,
invece, chiamare i precedenti record biblioteca, oppure anagrafe, o canile. Una biblioteca è una
collezione di libri, ognuno dei quali costituisce un singolo individuo dell’archivio. Allo stesso
modo, l’anagrafe è la collezione dei dati di tutti i cittadini, non di uno solo. Chiaro?
ESERCIZIO: pensare a un qualsiasi archivio, e definire la struttura dei suoi record.
Utilizzo del tipo di dato record
Come abbiamo detto, la definizione di un record non ci permette ancora di memorizzare dei dati al
suo interno, piuttosto, definisce un nuovo tipo di dato. Per poter memorizzare informazioni in un
record, abbiamo bisogno di una variabile del nuovo tipo che abbiamo definito con la struct.
Esattamente come dichiariamo variabili di ogni altro tipo di dato in C:
tipo nome;
come ad esempio:
int N;
char nome[20];
possiamo dichiarare una variabile di tipo record scrivendo
struct nome_struct nome_variabile;
4
Ad esempio, facendo riferimento al record libro definito precedentemente, dichiariamo una
variabile di tipo struct libro che si chiama un_libro:
struct libro un_libro;
L’effetto della dichiarazione della variabile un_libro di tipo struct libro è quello di riservare
memoria in RAM, come si vede dalla seguente figura:
Per fare un altro esempio, facendo riferimento al record cane definito precedentemente, dichiariamo
due variabili che si chiamano primo_cane e secondo_cane:
struct cane primo_cane, secondo_cane;
avremo così riservato spazio in RAM per memorizzare i dati di due cani.
Programmando, può risultare abbastanza noioso dover ripetere la parola struct ogni qualvolta si
vuole utilizzare il nuovo tipo di dato. Questo avviene nelle dichiarazioni di variabili, ma anche
quando si vuole passare un record come parametro a una funzione (ricordate? Ogni parametro
formale va definito con il suo tipo!). Per ovviare a questo inconveniente, il linguaggio C permette di
effettuare una definizione esplicita di tipo, con la quale è possibile assegnare un nuovo nome al
nuovo tipo appena definito, nel seguente modo:
typedef struct nome nuovo_nome;
Ad esempio, possiamo dare il nuovo nome book al tipo di dato struct libro definito precedentemente
typedef struct libro book;
A questo punto, il nuovo tipo di dato record che abbiamo definito si chiama book. Per dichiarare
una variabile del nuovo tipo, supponiamo di nome un_altro_libro, basta scrivere
book un_altro_libro;
senza dover ripetere la parola struct.
ESERCIZIO: pensare a un qualsiasi archivio, definire la struttura dei suoi record ed effettuare la
definizione esplicita di tipo.
5
Come accedere ai campi di un record in memoria?
Per lavorare con i record occorre poter accedere ai suoi dati interni, cioè i suoi campi. Per far
questo, si utilizza l’operatore . (punto), nel seguente modo:
nome_del_record . nome_del_campo
Ad esempio, se vogliamo caricare in memoria nella variabile un_libro i dati di un particolare libro,
dovremo scrivere:
printf(“inserisci il titolo del libro: ”);
scanf(“%s”, un_libro.titolo);
printf(“inserisci l’autore del libro: ”);
scanf(“%s”, un_libro.autore);
printf(“inserisci l’anno di pubblicazione del libro: ”);
scanf(“%d”, &un_libro.anno);
printf(“inserisci la casa editrice: ”);
scanf(“%s”, un_libro.editore);
Oppure, se vogliamo controllare se il titolo del libro memorizzato è lo stesso di un titolo chiesto in
input, dopo aver dichiarato una variabile dello stesso tipo del campo titolo
char titolo_da_cercare[50];
dovremo scrivere, ricordando di usare la strcmp per il confronto tra due stringhe:
printf(“quale titolo cerchi? ”);
scanf(“%s”, titolo_da_cercare);
if (!strcmp(titolo_da_cercare, un_libro.titolo))
printf(“e’ il libro che stavi cercando!”);
Facciamo un altro esempio: supponiamo di avere in RAM un record un_cane del tipo struct cane
definito in un esempio precedente (anche in questo caso avremmo potuto utilizzare una typedef per
non dover ripetere la parola chiave struct), e di voler visualizzare il suo campo eta. Basterà scrivere
la seguente istruzione:
printf(“%d”, un_cane.eta);
Per concludere questa trattazione sui record, diciamo che i campi di un record possono essere di
qualsiasi tipo, tra tutti quelli che abbiamo visto. Quindi, potremmo avere un campo di tipo intero,
reale, carattere, stringa (vettore di caratteri), array, o anche un altro record. Tuttavia, è più
opportuno utilizzare dati semplici e non strutturati come campi di un record, per una più facile
gestione degli archivi stessi.
Quando un campo di un record è una struttura dati e non un dato semplice, bisogna fare attenzione
alla notazione da usare per accedere alle informazioni. Facciamo qualche esempio:
6
struct cittadino
{
char
char
int
char
};
/* definizione del tipo record */
nome[20];
cognome[20];
data_di_nascita[3];
domicilio[50];
/* l’array contiene tre interi: giorno, mese, anno,
rispettivamente in posizione 0, 1, e 2 */
typedef struct cittadino citizen;
/* definizione esplicita di tipo */
citizen c;
/* dichiarazione della variabile c di tipo record */
printf(“nome: %s”, c.nome);
/* visualizza il nome del cittadino */
printf(“giorno di nascita: %d”, c.data_di_nascita[0]);
/* visualizza il giorno di nascita */
printf(“mese di nascita: %d”, c.data_di_nascita[1]);
/* visualizza il mese di nascita */
printf(“anno di nascita: %d”, c.data_di_nascita[2]);
/* visualizza l’anno di nascita */
ESERCIZIO: definire la struttura di un record, dichiarare una variabile di tipo record, caricare i suoi
dati e poi visualizzarli sul monitor.
Assegnamento multiplo
A differenza degli array, i record permettono l’assegnamento multiplo, ossia, è possibile assegnare
un record a un altro record, assegnando in una sola volta tutti i dati in esso contenuto. Facciamo un
esempio: sia dato il seguente record
struct libro
{
char
titolo[50];
char
autore[20];
int
anno;
char
editore[20];
};
typedef struct libro book;
book b1, b2;
Supponiamo di aver già caricato i campi del libro b1, con i dati
b1.titolo = “L’idiota”
b1.autore = “Dostoevskij”
b1.anno = “1869”
b1.editore = “Einaudi”
Possiamo, con una sola istruzione, assegnare il record b1 al record b2
b2 = b1;
e ritrovarci gli stessi valori nei campi del record b2.
7
Si noti che questo non è possibile con gli array. Se si vogliono assegnare i valori contenuti in un
vettore a un altro vettore, occorre assegnare uno per uno ogni elemento! Cioè, ad esempio, se
abbiamo
int vet1[10],vet2[10];
e vogliamo assegnare al vettore v2 il vettore v1, è sbagliato scrivere
vet2 = vet1;
mentre bisogna eseguire un ciclo per assegnare, uno per uno, tutti gli elementi
for(i=0; i<10; i++)
vet2[i] = vet1[i];
ESERCIZIO: definire la struttura di un record, dichiarare due variabili V1 e V2 di tipo record,
caricare i dati del record V1, assegnare il record V1 al record V2, visualizzare i dati del record V2.
Scarica