Esercizio 1 - Intranet DEIB 1 Scrivere un programma C che legge da tastiera una sequenza di numeri...

42
Esercizio 1 Scrivere un programma C che legge da tastiera una sequenza di numeri reali; la lettura termina quando la somma dei numeri immessi è maggiore di 50, e comunque non si possono immettere più di 100 numeri (se anche dopo avere immesso 100 numeri la loro somma non supera 50 la lettura termina comunque). Dopo avere letto tutti i numeri, se l’utente ha inserito almeno 3 valori, cercare se esiste una coppia di numeri tali che il loro rapporto (o il suo inverso) sia uguale al primo numero immesso e, se esiste, stamparla. Esempio di funzionamento del programma: Inserisci numero: 6.25 Inserisci numero: -2.5 Inserisci numero: 20 Inserisci numero: 13.863 Inserisci numero: -15.625 Inserisci numero: 4 Inserisci numero: 38.192 Il rapporto (o il suo inverso) tra -2.5 e -15.625 vale 6.25. Soluzione #include <stdio.h> typedef enum{false, true} bool; #define MAX_NUM 100 #define MAX_SUM 50 #define TOL 0.00000001 main(){ float dati[MAX_NUM], sum = 0, rapp, inv_rapp; int i, j, n_dati; bool trovata = false; n_dati = 0; do { printf("Inserisci numero: "); scanf("%f", &dati[n_dati]); sum += dati[n_dati]; n_dati++; } while (sum <= MAX_SUM && n_dati < MAX_NUM); if (n_dati >= 3) { i = 1; while (!trovata && i<n_dati-1) { j = i+1; while (!trovata && j<n_dati) { if (dati[i] == 0 || dati[j] == 0) { if (dati[0] == 0) { trovata = true; } } else {

Transcript of Esercizio 1 - Intranet DEIB 1 Scrivere un programma C che legge da tastiera una sequenza di numeri...

Esercizio 1

Scrivere un programma C che legge da tastiera una sequenza di numeri reali; la lettura termina quando la somma dei numeri immessi è maggiore di 50, e comunque non si possono immettere più di 100 numeri (se anche dopo avere immesso 100 numeri la loro somma non supera 50 la lettura termina comunque). Dopo avere letto tutti i numeri, se l’utente ha inserito almeno 3 valori, cercare se esiste una coppia di numeri tali che il loro rapporto (o il suo inverso) sia uguale al primo numero immesso e, se esiste, stamparla.

Esempio di funzionamento del programma:

Inserisci numero: 6.25 Inserisci numero: -2.5 Inserisci numero: 20 Inserisci numero: 13.863 Inserisci numero: -15.625 Inserisci numero: 4 Inserisci numero: 38.192 Il rapporto (o il suo inverso) tra -2.5 e -15.625 vale 6.25.

Soluzione #include <stdio.h> typedef enum{false, true} bool; #define MAX_NUM 100 #define MAX_SUM 50 #define TOL 0.00000001 main(){ float dati[MAX_NUM], sum = 0, rapp, inv_rapp; int i, j, n_dati; bool trovata = false; n_dati = 0; do { printf("Inserisci numero: "); scanf("%f", &dati[n_dati]); sum += dati[n_dati]; n_dati++; } while (sum <= MAX_SUM && n_dati < MAX_NUM); if (n_dati >= 3) { i = 1; while (!trovata && i<n_dati-1) { j = i+1; while (!trovata && j<n_dati) { if (dati[i] == 0 || dati[j] == 0) { if (dati[0] == 0) { trovata = true; } } else {

rapp = dati[i]/dati[j]; inv_rapp = dati[j]/dati[i]; if (rapp - dati[0] < TOL && rapp - dati[0] > -TOL || inv_rapp - dati[0] < TOL && inv_rapp - da ti[0] > -TOL) { trovata = true; } } if (trovata == true) { printf(“Il rapporto (o il suo inverso) tr a %f e %f vale %f\n”, dati[i], dati[j], dati[0]); } j++; } i++; } } else { printf(“Sono stati inseriti solo %d elementi\n” , n_dati); } }

Esercizio 2

Si vogliono definire i tipi di dato che servono per contenere le informazioni di un pubblico registro automobilistico dedicato ai motoveicoli. I tipi da definire sono i seguenti.

TipoDatiMotoveicolo rappresenta i dati di un motoveicolo. Questi dati si compongono di: targa del motoveicolo (7 lettere), marca del motoveicolo (massimo 15 caratteri), modello (massimo 20 caratteri), cilindrata (in cc), potenza (in kW), categoria (motorino, scooter, motocicletta, motocarro).

TipoDatiProprietario rappresenta i dati di una persona (il proprietario del motoveicolo): nome (massimo 30 caratteri), cognome (massimo 40 caratteri), codice fiscale (16 caratteri).

TipoVocePRA rappresenta una singola voce nel registro automobilistico; una voce si compone di 2 elementi, i dati del proprietario del motoveicolo ed i dati del motoveicolo stesso.

TipoPRA rappresenta un tipo adatto a contenere i dati di un PRA. Questo tipo di dati è un elenco di voci del PRA (si suppone che un PRA non possa contenere più di 10.000 elementi), più un contatore che dice quante voci sono effettivamente presenti nel PRA.

(se si ritiene utile, si possono definire altri tipi di dati, di supporto a quelli richiesti)

Soluzione typedef enum {motorino, scooter, motocicletta, motocarro} TipoCategoria; typedef struct { char targa[7] char marca[15] char modello[20]

int cilindrata; float potenza; TipoCategoria categoria; } TipoDatiMotoveicolo; typedef struct { char nome[30]; char cognome[40]; char codice_fiscale[16]; } TipoDatiProprietario; typedef struct { TipoDatiMotoveicolo motoveicolo; TipoDatiProprietario proprietario; } TipoVocePRA;

typedef struct { TipoVocePRA elementi[10000]; int n_elementi; } TipoPRA;

Esercizio 3

Siano P, Q, R, tre puntatori a interi e x, y due variabili intere. Si dica quanto valgono rispettivamente x, y, *P, *Q, *R dopo l’esecuzione della seguente sequenza di istruzioni.

x = 3; y = 5;

P = &x; Q = &y; R = P;

*R = 10; y = x + *Q; x = x + *P;

Q = R; P = Q

Soluzione

Al termine dell’esecuzione della sequenza di istruzioni le variabili valgono:

x = 20, y = 15, P, Q, R puntano tutti a x e quindi *P = *Q = *R = 20.

Esercizio 4

Scrivere un programma C che esegue le seguenti operazioni.

Legge da tastiera una sequenza di esattamente 64 pacchetti di 4 bit ciascuno (si ricorda che un pacchetto di 4 bit altro non è che una sequenza di 4 interi che possono assumere solo i valori 0 o 1). Un pacchetto è ammissibile solo se contiene solo 0 e 1; pacchetti non ammissibili vanno reimmessi.

Un pacchetto ammissibile è detto corretto se e solo se il numero di 1 in tutto il pacchetto è pari.

Il programma, quindi, per ogni pacchetto ammissibile, verifica se il pacchetto è corretto, e, dopo avere letto tutti i pacchetti, stampa prima i pacchetti corretti, quindi quelli scorretti.

Soluzione #define MAX_PACC 64 typedef int TipoPacchetto[4]; typedef enum {false, true} bool; main() { TipoPacchetto corretti[MAX_PACC], non_corretti[M AX_PACC], corrente; int i, j, k, h, somma, n_corretti, n_non_corretti; bool ammissibile; i = 0; j = 0; k = 0; while(i<MAX_PACC) { printf(“Pacchetto numero %d (4 bit, separare i singoli bit con degli spazi): ”, i+1); scanf(“%d %d %d %d”, &corrente[0], &corrente[1] , &corrente[2], &corrente[3]); ammissibile = true; somma = 0; for(h=0 ; h<4 ; h++) { if(corrente[h] > 1 || corrente[h] < 0) { ammissibile = false; } else { somma += corrente[h]; } } if(ammissibile) { if (somma % 2 == 0) { for(h=0 ; h<4 ; h++) { corretti[j][h] = corrente[h];} j++; } else { for(h=0 ; h<4 ; h++) { non_corretti[k][h] = corrente[h ];} k++; } i++; } else { printf(“Pacchetto immesso non ammissibile, re immetterlo!\n”); } } n_corretti = j; n_non_corretti = k; printf(“Pacchetti corretti:\n”); for(j=0 ; j<n_corretti ; j++) { for(h=0 ; h<4 ; h++) {printf(“%d”, corretti[j][h]); } printf(“\n”); } printf(“Pacchetti NON corretti:\n”); for(j=0 ; j<n_non_corretti ; j++) { for(h=0 ; h<4 ; h++) {printf(“%d”, non_corretti[j][h]) ; } printf(“\n”);

} }

Esercizio 5

Si abbia un file (binario) FileFatture contenente fatture. Ogni fattura sia del tipo TipoFatture dichiarato qui sotto.

typedef struct { unsigned int NumFattura; char Nome[30]; char Cognome[40]; float Importo; char PartitaIva [12]; …/*altri campi irrilevanti per l’esercizio*/ } TipoFatture;

Si scriva una procedura che aggiorna una (e una sola!) fattura del file cambiandone l’importo da Lire a Euro (si ricorda che un Euro vale 1936,27 Lire). La procedura riceve come parametro il numero d’ordine della fattura nel file (si assuma che il numero di fattura –denotato dal campo NumFattura- sia anche la posizione assunta dalla fattura nel file, cioè che la fattura numero 1 sia la prima, quella numero 2 la seconda, ecc.).

Prima di procedere alla codifica della procedura si dichiarino eventuali variabili globali da essa utilizzate.

Si supponga che il file sia già aperto al momento della chiamata della procedura.

Soluzione

FILE *file_fatture; void aggiorna( unsigned int num_ord) { TipoFatture fattura; long pos_corr; pos_corr = ftell(file_fatture); fseek(file_fatture, sizeof(TipoFatture)*(num_ord-1), SEEK_SET); fread(&fattura, sizeof(TipoFattura), 1, file_fatture); fattura.Importo = fattura.Importo/1936.27; fseek(file_fatture, sizeof(TipoFatture)*(num_ord-1), SEEK_SET); fwrite(&fattura, sizeof(TipoFattura), 1, file_fatture); fseek(file_fatture, pos_corr, SEEK_SET); /*anche se non esplicitamente richiesto, la procedu ra lascia inalterata la posizione corrente del file*/ }

Esercizio 6

Si definisca un tipo di dato Giocattolo costituito dal nome del giocattolo (ad esempio “Trenino_elettrico”), dalla data di fabbricazione, e dal prezzo. Se necessario, si definiscano in precedenza ulteriori tipi di dato utili per la definizione del tipo Giocattolo.

Si definisca poi un nuovo tipo Bambino, costituito dal nome del bambino e da una sequenza GiocattoliPosseduti. Tale sequenza è costituita a sua volta da un array di 10 elementi, ognuno dei quali essendo un puntatore al tipo Giocattolo.

Si dichiarino successivamente due variabili, Giocattoli e Bambini, consistenti in due array, rispettivamante di MaxGiocattoli elementi del tipo Giocattolo e MaxBambini elementi del tipo Bambino. MaxGiocattoli e MaxBambini, pure da definire, sono i due valori costanti 20 e 30.

Si può assumere che nè i nomi dei giocattoli, nè i nomi dei bambini siani più lunghi di 30 caratteri.

Soluzione #define MaxGiocattoli 20 #define MaxBambini 30 typedef struct { int giorno; int mese; int anno; } Data; typedef char string[30]; typedef struct { string nome; Data dataFabbricazione; float prezzo; } Giocattolo; typedef struct { string nome; Giocattolo *GiocattoliPosseduti[10]; } Bambino; Giocattolo Giocattoli[MaxGiocattoli]; Bambino Bambini[MaxBambini];

Esercizio 7

Con riferimento all’esercizio 6, si scriva un frammento di programma C che:

Legga da tastiera, mediante opportuno dialogo con l’utente, il nome di un bambino e, se questo compare nell’elenco dei bambini, stampa l’elenco dei nomi dei giocattoli da lui posseduti.

Nello scrivere il frammento di programma (ossia una sequenza di istruzioni che non costituisce necessariamente un programma completo) si assuma che l’elenco dei bambini e l’elenco dei giocattoli siano costituiti dalle variabili Bambini e Giocattoli già in memoria.

Per semplicità si può assumere che l’utente non commetta errori durante il dialogo con la macchina (ad esempio non fornisca nomi troppo lunghi).

Si facciano inoltre le seguenti assunzioni:

• Ogni stringa di caratteri che costituisca un nome (di giocattolo o di bambino) sia terminata dal carattere “A capo” (‘\r’)

• Se il bambino possiede solo k giocattoli, k < 10, gli ultimi 10 – k puntatori valgono NULL.

• La variabile NumBambini, che pure si assume sia già in memoria, contiene il numero di bambini effettivamente presenti nell’elenco dei bambini.

Un esempio di dialogo utente-programma è il seguente (NB: dopo aver battuto i caratteri che compongono il nome del bambino l’utente batte il tasto “A capo”)

--------------------------------------------------------------------------

Immetti il nome di un bambino:

Giovanni

I giocattoli posseduti da Giovanni sono:

Trenino_elettrico

Cavallo_a_dondolo

--------------------------------------------------------------------

Soluzione #include <stdio.h> #define …. typedef …. main () { Giocattolo Giocattoli[MaxGiocattoli]; Bambino Bambini[MaxBambini]; string NomeBambino; boolean trovato; int i, k, j, LunghNomeBamb, NumBambini; /* NumBambini indica il numero di bambini present i nell’elenco*/ ... /* parte di programma omessa. Comprenderà, tra l’ altro, * il dialogo utente-macchina per acquisire i dat i e memorizzarli * nelle variabili Giocattoli e Bambini*/ printf (“Immetti il nome del bambino di cui vuoi conoscere i giocattoli:\n”); i = 0; scanf("%c", & NomeBambino[i]); while (NomeBambino[i] != '\r') { i++; scanf("%c", &NomeBambino[i]) } LunghNomeBamb = i; /*Si cerca nell’array Bambini il nome immesso*/ trovato = false; k = 0;

while (! trovato && k < NumBambini) { for(i = 0; i <= LunghNomeBamb && Bambini[k].nome[i] == NomeBambino[i ]; i++){}; /*Si incrementa i finché la condizione rimane v era*/ if (i > LunghNomeBamb){ trovato = true; } else { k++; } } if (! trovato){ printf (“Il bambino indicato non compare nell’e lenco\n”); } else { printf (“I giocattoli posseduti dal bamino sono :\n”); i = 0; while (i<10 && Bambini[k].GiocattoliPosseduti[i] != NULL ) { for (j = 0; j < 30 && Bambini[k].GiocattoliPosseduti[i]- >nome[j] != ‘\r’; j++){ printf (“%c”, Bambini[k].GiocattoliPosseduti [i] -> nome[j]); } i++; printf (“\n”), } } /*fine del frammento di programma*/ ... }

Esercizio 8

Scrivere un programma che legge da tastiera due sequenze di caratteri alfabetici minuscoli (non sono ammessi numeri, segni di punteggiatura, ecc.) separate dal carattere di spazio ed anche terminate dal carattere di spazio, e dice all'utente se le due parole sono una l'anagramma dell'altro. Nella sequenza sono ammesse ripetizioni della stessa lettera. Si trascuri il fatto che le parole possano avere un significato in italiano oppure no.

Si supponga pure che l'utente non inserisca mai parole più lunghe di 50 caratteri. Se l'utente inserisce simboli non ammessi, questi vanno ignorati.

Esempio di funzionamento:

Inserisci le parole (separate e terminate da uno spazio):

terkmit<spazio>mertkit<spazio>

Le due parole sono una l'anagramma dell'altra.

Soluzione main () { char prima_parola[50], seconda_parola[50];

char curr; int l1 = 0, l2 = 0, i, j; int sono_anagrammi, trovato; printf("Inserisci le parole (separate e terminate da uno spazio)\n"); do { scanf("%c", &curr); if (curr >= 'a' && curr <= 'z') { prima_parola[l1] = curr; l1++; } } while(l1<50 && curr != ' '); do { scanf("%c", &curr); if (curr >= 'a' && curr <= 'z') { seconda_parola[l2] = curr; l2++; } } while(l2<50 && curr != ' '); sono_anagrammi = 1; i = 0; while(sono_anagrammi && i<l1){ trovato = 0; j = 0; while(!trovato && i<l2){ if(prima_parola[l1] == seconda_parola[l2]){ trovato = 1; seconda_parola[l2] = '#'; } j++; if (!trovato) sono_anagrammi = 0; } i++; } if(sono_anagrammi == 1) printf("Le due parole sono una l'anagramma dell 'altra.\n"); else printf("Le due parole NON sono una l'anagramma dell'altra.\n"); }

Esercizo 9

Si definisca(no) i(l) tipi(o) di dato che serve(ono) per contenere le informazioni relative al listino prezzi di un concessionario di motocicli che fa anche officina meccanica. Per ogni modello di motociclo venduto devono essere definiti:

nome del modello (massimo 30 caratteri), marca (massimo 20 caratteri), codice identificativo (10 cifre), prezzo, cilindrata (in cc), elenco dei pezzi sostituibili del motociclo. Di ogni pezzo sostituibile devono essere definiti: descrizione del pezzo (massimo 100 caratteri), codice identificativo (10 cifre), prezzo, numero di giorni in cui il pezzo arriva se ordinato, ore di lavoro necessarie per la sostituzione.

Si supponga pur che il numero delle parti sostituibili di un motociclo sia al massimo 50.

Soluzione typedef char TipoCodice[10]; typedef struct { char descrizione[100]; TipoCodice codice; unsigned int prezzo;

unsigned int ritardo_ordine; unsigned int ore_lavoro; } TipoPezzo; typedef struct { char nome[30]; char marca[20]; TipoCodice codice; unsigned int prezzo; unsigned int cilindrata; TipoPezzo pezzi_sost[50]; unsigned int n_pezzi_sost; } TipoMotociclo;

Esercizio 10

Con riferimento all'esercizio 9 si scriva un sottoprogramma che prende in ingresso la descrizione di un motociclo ed un elenco di codici di pezzi da sostituire, e ritorna:

• il costo totale della sostituzione, sapendo che ogni ora di lavoro costa 20 euro;

• il numero di giorni che bisogna aspettare per avere la riparazione efettuata nell'ipotesi che tutti i pezzi siano da ordinare, e che la riparazione possa essere fatta in giornata una volta che tutti i pezzi arrivano;

• un codice che è 0 se il calcolo fallisce (per esempio se uno dei codici di pezzo da sostituire non è in realtà un pezzo del motociclo), 1 altrimenti.

Qualunque sottoprogramma ausiliaro (che non faccia parte della libreria standard del C) che viene eventualmente usato nel sottoprogramma precedente deve essere definito nella sua interezza.

Soluzione int calcolaCosto (TipoMotociclo moto, TipoCodice pezzi [], unsigned int npezzi, unsigned int *costo, unsigned int *giorni){ TipoPezzo pezzo; int i; unsigned int c_parz = 0, g_parz = 0; for (i = 0; i<npezzi ; i++){ if(trovaPezzo(moto.pezzi_sost, moto.npezzi, pezzi[i], &pezzo) == 0){ return 0; } c_parz += pezzo.prezzo; if (pezzo.ritardo_ordine > g_parz){ g_parz = pezzo.ritardo_ordine; } } *costo = c_parz; *giorni = g_parz + 1; /* contiamo anche il giorno per la riparazione */

return 1; } /* La seguente funzione prende in ingresso un elenc o di pezzi * ed un codice di pezzo e ritorna il tipo di pezzo presente * nella lista (se presente). * Se il codice non e' presente nella lista ritorna 0, altrimenti 1. */ int trovaPezzo( TipoPezzo pezzi[], unsigned int npezzi, TipoCodice cod, TipoPezzo *pezzo){ int i; if (pezzi == NULL) return 0; for (i = 0 ; i<npezzi; i++){ if (strcmp(pezzi[i].codice, cod) == 0) { *pezzo = pezzi[i]; return 1; } } return 0; }

Esercizio 11

Definire un tipo di dato TipoStrumentista che descrive i dati relativi ad un musicista jazz. Ogni musicista è definito da un nome (massimo 40 caratteri), da una data di nascita, e dallo strumento che suona. Ogni musicista suona un solo strumento, ed il tipo di strumento suonato può essere solo uno dei seguenti (non sono ammessi altri strumenti): pianoforte, basso, batteria, sax, tromba, trombone, flauto, clarinetto, voce.

Definire un tipo TipoQuartetto che contiene i dati relativi ad un quartetto di musicisti. Ogni quartetto è caratterizzato da un nome (al massimo 30 caratteri) e da esattamente 4 musicisti (non uno di più, non uno di meno).

Definire infine una variabile ElencoQuartetti che può contenere al massimo 100 quartetti.

(se si ritiene utile, si possono definire altri tipi di dati, di supporto a quelli richiesti)

Soluzione #define L_NOME_MUS 40 #define L_NOME_QUAR 30 #define MAX_QUARTETTI 100 typedef struct { short giorno; short mese; int anno; } TipoData; typedef enum {pianoforte, basso, batteria, sax, trombone, flaut o, clarinetto, voce} TipoStrumento;

typedef struct { char nome[L_NOME_MUS+1]; TipoData data_nascita; TipoStrumento strumento; } TipoStrumentista; typedef struct { char nome[L_NOME_QUAR+1]; TipoStrumentista musicisti[4]; } TipoQuartetto; TipoQuartetto ElencoQuartetti[MAX_QUARTETTI];

Esercizio 12

Scrivere un frammento di programma C che, per ogni quartetto senza pianoforte (cioè in cui nessuno strumentista suona il pianoforte) presente nell'elenco ElencoQuartetti , stampa a video il nome del quartetto. In seguito stampa a video il nome di tutti i quartetti con pianoforte.

Nel realizzare il frammento di programma si supponga che la variabile ElencoQuartetti di cui al punto precedente sia già stata inizializzata (sia cioè già stata caricata di dati, che non devono essere riletti da tastiera), e si supponga inoltre che un'altra variabile numQuartetti , di tipo int (anch'essa già inizializzata) contenga il numero di quartetti effettivamente inseriti in ElencoQuartetti .

Soluzione main() { TipoQuartetto ElencoQuartetti[MAX_QUARTETTI]; int numQuartetti[MAX_QUARTETTI]; /* inizializzazione delle variabili sopra dichiar ate, * da considerare già fatta */ TipoQuartetto QuartettiSenzaPiano[MAX_QUARTETTI]; TipoQuartetto QuartettiConPiano[MAX_QUARTETTI]; int numQuarSenzaPiano = 0, numQuarConPiano = 0, i, j, trovato; for (i=0; i<numQuartetti; i++){ j = 0; trovato = 0; while (trovato == 0 && j<4){ if (ElencoQuartetti[i].musicisti[j].strumento == pian oforte){ trovato = 1; } j++; } if (trovato == 0) { QuartettiSenzaPiano[numQuarSenzaPiano] = Elen coQuartetti[i]; numQuarSenzaPiano++; } else { QuartettiConPiano[numQuarConPiano] = ElencoQu artetti[i]; numQuarConPiano++;

} } printf(“Quartetti senza piano:\n”); for (i=0; i<numQuarSenzaPiano; i++){ printf(“%s\n”, QuartettiSenzaPiano[i].nome); } printf(“Quartetti con piano:\n”); for (i=0; i<numQuarConPiano; i++){ printf(“%s\n”, QuartettiConPiano[i].nome); }

Esercizio 13

Si definiscano i tipi TipoStrumentista e TipoQuartetto come nell'esercizio 11.

Si definisca inoltre un tipo TipoConcerto costituito dal nome della località in cui il concerto si è tenuto (massimo 40 caratteri), dalla data in cui si è tenuto il concerto, e dal quartetto che ha tenuto il concerto.

Soluzione #define L_NOME_MUS 40 #define L_NOME_QUAR 30 #define MAX_QUARTETTI 100 typedef struct { short giorno; short mese; int anno; } TipoData; typedef enum {pianoforte, basso, batteria, sax, trombone, flaut o, clarinetto, voce} TipoStrumento; typedef struct { char nome[L_NOME_MUS+1]; TipoData data_nascita; TipoStrumento strumento; } TipoStrumentista; typedef struct { char nome[L_NOME_QUAR+1]; TipoStrumentista musicisti[4]; } TipoQuartetto; #define L_NOME_CONC 40 typedef struct { char nome_loc[L_NOME_CONC+1]; TipoData data; TipoQuartetto quartetto; } TipoConcerto;

Esercizio 14

Si supponga di avere un file binario contenente un elenco di concerti. Si supponga inoltre che il file sia già stato aperto in modalità (binaria) lettura/scrittura.

Si risolva uno (ed uno solo) dei seguenti punti.

Variante a

Dopo avere dichiarato eventuali variabili globali, definire una funzione CancellaConcertiDiQuartetto che riceve in ingresso il nome di un quartetto, e crea un nuovo file (sempre binario), di nome NuovaListaConcerti.dat , contenente l'elenco di tutti i concerti esclusi quelli del quartetto il cui nome è stato passato alla funzione. La funzione ritorna 1 se l'operazione è andata a buon fine, 0 altrimenti. Il file originario con l'elenco dei concerti rimane immutato.

Variante b

Dopo avere dichiarato eventuali variabili globali, definire una funzione CancellaConcertiDiQuartetto che riceve in ingresso il nome di un quartetto, e cancella dal file con l'elenco dei concerti tutti i concerti tenuti dal quartetto il cui come è stato passato alla funzione. La funzione ritorna 1 se l'operazione è andata a buon fine, 0 altrimenti; essa modifica il file originario con l'elenco dei concerti.

In questa variante è ammesso (anzi, è consigliato) aprire file temporanei, in aggiunta al file originario.

Variante c

Dopo avere dichiarato eventuali variabili globali, definire una funzione CancellaConcertiDiQuartetto che riceve in ingresso il nome di un quartetto, e cancella dal file con l'elenco dei concerti tutti i concerti tenuti dal quartetto il cui come è stato passato alla funzione. La funzione ritorna 1 se l'operazione è andata a buon fine, 0 altrimenti; essa modifica il file originario con l'elenco dei concerti.

In questa variante non è ammesso aprire file temporanei aggiuntivi, si può lavorare solo sul file originario.

Aiuto per le varianti b e c: per troncare un file f alla lunghezza data dalla posizione corrente nel file, l’istruzione da usare è la seguente:

ftruncate(fileno(f), ftell(f));

Soluzione variante a #include <string.h> #include <stdio.h> FILE *f_concerti; int CancellaConcertiDiQuartetto( char nome_quar[]){ TipoConcerto curr_conc;

FILE *nf = fopen(“NuovaListaConcerti.dat”, “wb”); if (nf == NULL) return 0; rewind(f_concerti); while (!feof(f_concerti)){ if (fread(&curr_conc, sizeof(TipoConcerto), 1, f_concerti) == 1){ if (strcmp(curr_conc.quartetto.nome, nome_quar) != 0) { if (fwrite(&curr_conc, sizeof(TipoConcerto), 1, nf) != 1){ fclose(nf); return 0; } } } else { fclose(nf); return 0; } } if (fclose(nf) == 0) return 1; else return 0; }

Soluzione variante b #include <string.h> #include <stdio.h> FILE *f_concerti; int CancellaConcertiDiQuartetto( char nome_quar[]){ TipoConcerto curr_conc; FILE *tmp_f = fopen(“_temp.dat”, “wb+”); if (tmp_f == NULL) return 0; rewind(f_concerti); while (!feof(f_concerti)){ if (fread(&curr_conc, sizeof(TipoConcerto), 1, f_concerti) == 1){ if (strcmp(curr_conc.quartetto.nome, nome_quar) != 0) { if (fwrite(&curr_conc, sizeof(TipoConcerto), 1, tmp_f) != 1){ fclose(tmp_f); return 0; } } } else { fclose(tmp_f); return 0; } } rewind(f_concerti); rewind(tmp_f); while (!feof(tmp_f)){ if (fread(&curr_conc, sizeof(TipoConcerto), 1, tmp_f) == 1) { if (fwrite(&curr_conc, sizeof(TipoConcerto), 1, f_concerti) != 1) { fclose(tmp_f); return 0; } } else { fclose(tmp_f); return 0; } } /* tronco il file all’ultima posizione in cui ho scritto * (il file riscritto è in generale più corto * del file originario) */ ftruncate(fileno(f_concerti), ftell(f_concerti)); if (fclose(tmp_f) == 0) return 1; else return 0; }

Soluzione variante c #include <string.h> #include <stdio.h> FILE *f_concerti; int CancellaConcertiDiQuartetto( char nome_quar[]){ TipoConcerto curr_conc; /* tengo due indici nel file: la prossima posizio ne in cui devo * scrivere (pos_next_write) e la prossima posizi one da cui devo * leggere (pos_next_read) */ long pos_next_write, pos_next_read; rewind(f_concerti); pos_next_write = ftell(f_concerti); while (!feof(f_concerti)){ if (fread(&curr_conc, sizeof(TipoConcerto), 1, f_concerti) == 1) { if (strcmp(curr_conc.quartetto.nome, nome_quar) != 0) { /* se il concerto è da mantenere, lo riscri vo nel file * f_concerti alla prossima posizione in cu i devo scrivere. * Prima di scrivere, però, devo memorizzar e la poszione * corrente nel file, perchè a questa posiz ione dovrò poi * tornare per ricominciare a leggere. */ pos_next_read = ftell(f_concerti); /* mi sposto nel file alla posizione in cui devo scrivere, * e poi effettivamente scrivo. */ if (fseek(f_concerti, pos_next_write, SEEK_SET)) return 0; if (fwrite(&curr_conc, sizeof(TipoConcerto), 1, f_concerti) != 1) return 0; /* memorizzo la posizione corrente, che è l a prossima posizione * in cui dovrò scrivere, e mi riporto nell a posizione che è la * prossima da cui devo leggere */ pos_next_write = ftell(f_concerti); if (fseek(f_concerti, pos_next_read, SEEK_SET)) return 0; } } else { return 0; } } /* mi riporto sulla posizione che è la prossima i n cui scrivere * (cioè appena dopo l’ultimo concerto che va con servato, e tronco il * file (il file riscritto è in generale più cort o del file originario */ if (fseek(f_concerti, pos_next_write, SEEK_SET)) return 0; ftruncate(fileno(f_concerti), ftell(f_concerti)); return 1; }

Esercizio 15

Si definisca un tipo di dato ContoCorrente contenente, tra l’altro, almeno i seguenti campi:

Numero del CC

Nome dell’intestatario

Importo presente sul CC

Si definisca poi un sottoprogramma che riceva come parametro un numero di CC e un aggiornamento opportunamente codificato (ad esempio, un numero relativo, oppure un qualificatore “deposito/prelievo” seguito da un valore assoluto) ed apporti l’aggiornamento richiesto ad un file, supposto già aperto ed il cui descrittore è contenuto in una variabile globale, di record di tipo ContoCorrente .

Come può cambiare il sottoprogramma nell’ipotesi che nel file il numero di CC coincida con la posizione occupata dal record (record relativo al CC # 0 in posizione 0, ecc.)?

Parte facoltativa

Si descriva sinteticamente, senza necessariamente codificare l’intero sottoprogramma, come si potrebbe modificare il sottoprogramma se i parametri di ingresso fossero numero di conto e nome dell’intestatario, ma uno dei due potesse mancare. Si considerino sia il caso in cui ogni persona possa essere intestatario al più di un solo CC sia il caso opposto. Sarebbe un sottoprogramma siffatto il modo migliore per affrontare il problema nella realtà quando l’utente si presenta allo sportello fornendo solo uno dei dati suddetti? In caso negativo che alternativa proporreste?

Soluzione typedef struct { int CC; char intestatario[50]; double importo; } ContoCorrente; typedef enum {fale, true} boolean; boolean aggiorna( int n_CC, double variazione){ ContoCorrente curr; while(fread(&curr, sizeof(ContoCorrente), 1, file) == 1){ if (curr.CC == n_CC){ curr.importo += variazione; fseek(file, - sizeof(ContoCorrente), SEEK_CUR); if(fwrite(&curr, sizeof(ContoCorrente), 1, file) == 1){ return true; } else { return false; } } } return false; }

Se il numero di conto corrente coincide con la posizione nel file, il programma va modificato come segue:

boolean aggiorna( int n_CC, double variazione){ ContoCorrente curr; if(fseek(file, sizeof(ContoCorrente)*n_CC, SEEK_SET) != 0){

return false; } if (fread(&curr, sizeof(ContoCorrente), 1, file) != 1 ) return false; curr.importo += variazione; fseek(file, - sizeof(ContoCorrente), SEEK_CUR); if(fwrite(&curr, sizeof(ContoCorrente), 1, file) == 1){ return true; } else { return false; } }

Soluzione Parte facoltativa

L’intestazione del sottoprogramma andrebbe modificata come segue:

boolean aggiorna( int *n_CC, char intestatario[], double variazione)

Il numero di conto corrente viene passato come puntatore adesso, per dare la possibilità di mettere NULL nel caso in cui non si voglia specificare alcun numero di conto corrente.

Nel caso in cui sia il numero di conto che il nome del correntista vengono passati al sottoprogramma, nel ciclo while che va a trovare i dati del conto corrente da modificare occorre verificare se entrambi coincidono con i dati del conto appena letto. In caso positivo, si effettua la modifica, altrimenti, se non è possibile che una persona abbia intestati più conti correnti, viene segnalato un errore. Se invece una persona può avere intestati più conti correnti, si continua nella ricerca.

Se invece uno solo tra numero di conto e nome dell’interstatario è specificato, la verifica viene fatta con quello che c’è e basta (e non è possibile fare la contro-verifica che i dati siano consistenti tra di loro). Se però è possibile che un correntista abbia più conti correnti e viene passato solo il nome dell’intestatario, occorre verificare se esistono più CC con lo stesso intestatario e, in tal caso, sollevare un errore; altrimenti si può procedere con l’aggiornamento.

Un sottoprogramma siffatto non sarebbe la soluzione migliore. La soluzione migliore sarebbe di avere tre sottoprogrammi separati, uno che prende in ingresso solo il numero di conto corrente, uno che accetta solo il nome dell’intestatario, ed uno che li accetta entrambi. Il main, o un sottoprogramma opportuno eventualmente interattivo, dovrebbe decidere quale dei sottoprogrammi è appropriato.

Esercizio 16

Si consideri un programma contenente, tra l’altro, la seguente dichiarazione

int x

si consideri poi la seguente sequenza di istruzioni appartenente al programma:

P1 = &x;

P = &P1;

*P1 = 10;

**P = *P1 + 2*x;

printf (…, **P);

Parte 1

Si completi la parte dichiarativa del programma con le dichiarazioni necessarie a far sì che il programma venga compilato con successo –senza segnalazioni di errore e neanche “warnings” ed eseguito. Si completi inoltre l’istruzione printf con la parte mancante indicata dai puntini, in modo che essa provochi la stampa del valore indicato da **P.

Parte 2

Assumendo che prima dell’esecuzione della sequenza di istruzioni x contenga il valore 5, si dica quale valore viene stampato dalla istruzione finale printf.

Soluzione parte 1 int *P1; int **P; printf (“Il contenuto della cella puntata dalla cel la puntata dal puntatore P è %d \n”, **P)

Soluzione parte 2

Dopo i primi due assegnamenti P1 punta a x e P punta a sua volta a P1. Il terzo assegnamento deposita perciò il valore 10 in x. Il quarto fa riferimento ancora ad x sia attraverso il doppio puntamento **P che attraverso il puntamento semplice P1. Perciò il valore 10 + 2*10 = 30 viene depositato in x e successivamente stampato.

Esercizio 17

Si scriva un programma C che esegue le seguenti operazioni. Legge da tastiera una sequenza di caratteri lunga al massimo 12 caratteri e terminata da '*' e determina se:

1. la sequenza corrisponde ad un numero di telefono (cioè se è composta solo da numeri, non da altri caratteri)

2. il numero corrisponde ad un cellulare o ad un telefono fisso (un numero di telefono fisso inizia sempre per '0', tutti gli altri si possono considerare numeri di cellulare).

Esempio di funzionamento del programma:

Inserisci numero: 02 2399 3561

Il numero immesso corrisponde ad un telefono fisso.

Soluzione #include <stidio.h> #define MAX_C 12 main() { char n_tel[MAX_C]; char curr; int i, n_c, flag; /* flag e' uguale a 1 se la sequenza di caratteri contiene * solo cifre, 0 altrimenti */ flag = 1; printf("Inserisci numero: "); n_c = 0; do { scanf("%c", &curr); if (curr != '*') { n_tel[n_c] = curr; n_c++; if (curr < '0' || curr > '9') { flag = 0; } } } while(n_c < MAX_C && curr != '*'); if (flag == 0 || n_c == 0) { printf ("la sequenza inserita NON corrisponde a d un numero di telefono\n"); } else { if (n_tel[0] == '0') { printf("Il numero immesso corrisponde ad un t elefono fisso\n"); } else { printf("Il numero immesso corrisponde ad un t elefono cellulare\n"); } } }

Esercizio 18

Siano date le variabili x1, x2, y1, y2, rispettivamente di tipo <tipo_x1>, <tipo_x2>, <tipo_y1>, <tipo_y2> ed i sottoprogrammi P1 e P2:

int P1(int **p1, int *p2, int p3){

x1 = x1 + **p1 + *p2 + p3;

return **p1 + *p2 + p3;

}

int P2(int **p1, int *p2, int p3){

x2 = x2 + **p1 + *p2 + p3;

p2 = &p3;

return **p1 + *p2 + p3;

}

Sia dato il corrispondente main:

int main(){

x1 = 1; y1 = &x1;

x2 = 1; y2 = &x2;

x1 = P1(&y1, y1, x1);

x2 = P2(&y2, y2, x2);

printf("Valore di x1: %<codice_x1>; \n Valore di x2: %<codice_x2>");

}

a. Si completi il programma costituito dai sottoprogrammi P1, P2 e main con adeguate parti dichiarative e direttive in modo che il programma possa essere compilato ed eseguito correttamente.

b. Si dica poi, spiegandone il perchè, quali sono i valori stampati dalla printf finale del main.

Soluzione punto a. /* variabili globali */ int x1; int x2; int main(){ int *y1; int *y2; /* ... */ printf("Valore di x1: % d; \n Valore di x2: % d"); }

Soluzione punto b.

Il valore di x1 è 9, quello di x2 6. Infatti, quando viene invocato P1 la situazione è la seguente:

x1

p1 p2 p3

1

1

y1

Siccome P1 non cambia la struttura dei puntatori, in P1 prima viene assegnato a x1 il valore 1+1+1+1=4, poi viene ritornato il valore 4+4+1 = 9.

Quando P2 viene invocato, invece, la situazione è la seguente:

x2

p1 p2 p3

1

1

y2

P2 però cambia la struttura dei puntatori dopo che a x2 è assegnato il valore 1+1+1+1=4, e la modifica come segue:

x2

p1 p2 p3

4

1

y2

Quindi P2 ritorna il valore 4+1+1=6.

Esercizio 19

Si definisca un tipo di dato ContoCorrente contenente, tra l’altro, almeno i seguenti campi:

Numero del CC

Nome dell’intestatario

Importo presente sul CC

Si definisca anche un tipo di dato AggiornamentoCC che descrive le operazioni di deposito/prelievo che possono venire effettuate su un conto corrente. AggiornamentoCC deve contenere le informazioni riguardo a quale conto corrente viene aggiornato, e al tipo di aggiornamento viene effettuato (deposito/prelievo e la somma depositata/prelevata).

Si definisca poi un sottoprogramma che riceve come parametri i descrittori di due file, uno contenente dei record di tipo ContoCorrente ed uno contenente dei record di tipo AggiornamentoCC . Sapendo che in entrambi i file i record sono ordinati in modo crescente secondo il numero di conto corrente, il sottoprogramma legge gli aggiornamenti dal file apposito, e li applica ai conti correnti i cui dati sono memorizzati nell'altro file.

Si supponga che:

• nel file con gli aggiornamenti non ci possano essere due aggiornamenti riguardanti lo stesso conto corrente;

• non necessariamente ci sia un aggiornamento per ogni conto corrente;

• ci possano essere aggiornamenti relativi a conti correnti inesistenti.

Soluzione typedef struct { int CC; char intestatario[50]; double importo; } ContoCorrente; typedef struct { int CC; double importoAgg; } AggiornamentoCC; typedef enum {false, true} bool; bool aggiornaDaFile(FILE *conti, FILE *agg){ ContoCorrente conto_corr; AggiornamentoCC agg_corr; bool stop = false; rewind(conti); rewind(agg); if (feof(conti) || feof(agg)) return true; if (fread(&conto_corr, sizeof(ContoCorrente), 1, conti) != 1) return false; if (fread(&agg_corr, sizeof(AggiornamentoCC), 1, agg) != 1) return false; while(!stop){ if (agg_corr.CC == conto_corr.CC){ conto_corr.importo += agg_corr.importo; fseek(conti, - sizeof(ContoCorrente), SEEK_CUR); if(fwrite(&curr, sizeof(ContoCorrente), 1, file) != 1) return false; if (feof(conti) || feof(agg)) { stop = true; } else { if (fread(&conto_corr, sizeof(ContoCorrente), 1, conti) != 1) return false; if (fread(&agg_corr, sizeof(AggiornamentoCC), 1, agg) != 1) return false; } } else if(agg_corr.CC < conto_corr.CC) { if (feof(agg)) { stop = true; } else { if (fread(&agg_corr, sizeof(AggiornamentoCC), 1, agg) != 1) return false;

} } else { if (feof(conti)) { stop = true; } else { if (fread(&conto_corr, sizeof(ContoCorrente), 1, conti) != 1) return false; } } } return true; }

Esercizio 20

Scrivere un programma che legge da tastiera una sequenza di esattamente 10 numeri razionali, ognuno formato da numeratore e denominatore (sia numeratore che denominatore devono essere numeri interi) e:

a) verifica che tutti abbiano denominatore diverso da zero;

b) se tutti i numeri hanno denominatore diverso da zero stampa a video:

a. la media dei numeri che sono interi (cioè con numeratore multiplo del denominatore)

b. il prodotto di tutti i numeri

Soluzione typedef struct { int num; int den; } TipoRazionale; typedef enum{false, true} bool; main() { TipoRazionale seq[10]; int i, int n_int = 0; double media = 0.0; TipoRazionale prod; bool flag = true; for (i=0; i<10; i++){ printf("inserisci numero razionale nella forma <numeratore/denominatore>: "); scanf("%d/%d", &seq[i].num, &seq[i].den); if (seq[i].den == 0) flag = false; } if (flag){ for (i=0; i<10; i++){ if (seq[i].num%seq[i].den == 0){ n_int++;

media += seq[i].num/seq[i].den; } prod.num = prod.num * seq[i].num; prod.den = prod.den * seq[i].den; } if (n_int > 0) media = media / n_int; printf("media numeri interi: %f\n", media); printf("prodotto numeri: %d/%d\n", prod.num, prod.den); } }

Esercizo 21

parte a

Si definisca(no) i(l) tipi(o) di dato che serve(ono) per contenere le informazioni relative alla carriera dei calciatori militanti in serie A. Per ogni calciatore devono essere definiti:

Nome, Cognome, Data di nascita, Elenco dei dati delle stagioni giocate in Serie A (per ogni stagione giocata in serie A deve essere definito: anno, squadra in cui si è giocato, numero di partite giocate, numero di gol segnati), Media dei gol segnati per anno.

Si assuma pure che un giocatore, in una stagione, gioca per al massimo una squadra.

parte b

Si scriva un sottoprogramma che prende in ingresso un file binario (supposto già aperto sia in letture che in scrittura) contenente i dati di tutti i calciatori di serie A, privi della media dei gol segnati per anno e lo aggiorna inserendo nell’apposito campo la media suddetta.

Soluzione parte a typedef struct { short giorno; short mese; unsigned int anno; } TipoData; typedef struct { char nome[30]; char cognome[30]; TipoData data_nascita; TipoStagione stagioni[30]; int n_stagioni; float media_gol; } TipoGiocatore; typedef struct { int anno; char squadra[20]; int n_partite; int n_gol; } TipoStagione;

Soluzione parte b void clacolaMedie(FILE *f){ rewind(f); TipoGiocatore g; while( fread(&g, sizeof(TipoGiocatore), 1, f) == 1){ g.media_gol = clacolaMediaGol(g.stagioni, n_sta gioni); fseek(biblio, -(sizeof(TipoGiocatore)), SEEK_CUR); fwrite(&g, sizeof(TipoGiocaotre), 1, f); } }

Laddove la funzione calcolaMediaGol è definita come segue: float clacolaMediaGol(TipoStagione s[], int num_stag){ int i; float somma = 0; for(i =0; i<num_stag; i++) somma += s[i].n_gol; if(num_stag > 0) return somma/n_stag else return 0; }

Esercizio 22

Si definiscano i seguenti tipi di dati.

IstitutoOspedaliero , caratterizzato da un nome e da un indirizzo, e che può essere o privato o pubblico.

Medico : ogni medico ha un nome e un indirizzo, e può essere o un medico della mutua, oppure no. Se il medico non è della mutua, questi afferisce ad un istituto ospedaliero (se invece il medico è della mutua, non afferisce ad alcun ospedale).

Paziente , caratterizzato da un nome e da un codice identificativo alfanumerico di 10 caratteri; ogni paziente ha un medico della mutua, ed ha al massimo 20 ricoveri in ospedale.

Un ricovero in ospedale (descritto dal tipo di dato Ricovero ) si caratterizza per una data di inizio ricovero, una data di fine ricovero, l'istituto ospedaliero in cui il ricovero è stato effettuato e, eventualmente, l'operazione chirurgica effettuata (l'operazione chiurgica durante un ricovero è opzionale, ci può essere oppure no).

Un'operazione chirurgica si caratterizza per la data in cui viene effettuata, il medico che la esegue, ed ha associata una descrizione a parole che dice di che cosa l'operazione è consistita.

Soluzione typedef enum{false, true} bool; typedef struct { char nome[50]; char indirizzo[70];

bool privato; } IstitutoOspedaliero; typedef struct { char nome[30]; char indirizzo[70]; bool dellaMutua; IstitutoOspedaliero *istituto; } Medico; typedef struct { char nome[30]; char id_sanitario[10]; Medico med; Ricovero ricoveri[20]; } Paziente; typedef struct { TipoData inizio; TipoData fine; IstitutoOspedaliero ospedale; Operazione *op; } Ricovero; typedef struct { TipoData data; Medico med; char descr[200]; } Operazione;

Esercizio 23

Sia dato un tipo di file di tipo testo (file di caratteri), contenente un elenco di record relativi a dei medici, ognuno con i seguenti dati: nome, indirizzo, si/no a seconda che il medico sia della mutua oppure no. Se il medico non è della mutua, il file contiene anche il nome e l'indirizzo dell'istituto ospedaliero a cui fa riferimento, ed il fatto che questo sia pubblico o privato ("si" se è pubblico, "no" se è privato).

Ogni dato (nome indirizzo, ecc.) si trova una riga diversa, e ogni record termina con una riga contenente un #.

Un esempio di record è il seguente:

Mario Ferrari

Corso Plebisciti, 12, Milano

si

#

Umberto Veronesi

via della Spiga, 4, Milano

no

Istituto Oncologico Europeo

via Ripamonti 435, Milano

no

#

Si scriva una procedura che prende in ingresso il nome di un file del tipo di cui sopra, e crea un file binario "istitutiOspedalieri" che contiene una serie di elementi di tipo IstitutoOspedaliero . Gli elementi di tipo IstitutoOspedaliero scritti nel file "istitutiOspedalieri" sono tutti e soli gli istituti che compaiono nel file di tipo testo il cui nome è ricevuto in ingresso, scritti in un ordine a discrezione dello studente.

Bonus di 3 punti se il file "istituti ospedalieri" è creato in modo che un istituto ospedaliero compaia al massimo una volta nel file (cioè se non ci sono ripetizioni di istituti ospedalieri).

Soluzione void scrivi_ospedali ( char file_medici[]){ FILE *f_med, *f_osp; char temp[100]; char mutua[3]; char privato[3]; IstitutoOspedaliero ist; f_med = fopen(file_medici, "r"); f_osp = fopen("istitutiOspedalieri", "wb"); while(!feof(f_med)){ /* le righe delle quali non mi interessa il con tenuto le memorizzo * in un array 'temp' grande * a sufficienza per contenere tutte le stringh e di interesse. */ fgets(temp, 30, f_med); fgets(temp, 70, f_med); fgets(mutua, 3, f_med); if(strcmp(mutua, "no") == 0){ fgets(ist.nome, 50, f_med); fgets(ist.indirizzo, 70, f_med); fgets(privato, 3, f_med); if(strcmp(privato, "si") == 0){ ist.privato = true; } else { ist.privato = false; } /* versione semplice, che non si preoccupa di evitare possibili * ripetizioni dello stesso istituto ospedali ero nel file di output. */ fwrite(&ist, sizeof(IstitutoOspedaliero), 1, f_osp); } /* suppongo che il formato del file sia corrett o, e non faccio alcuna * verifica sul fatto che la prossima riga cont enga esattamente un '#' * oppure no. Un'alternativa sarebbe di control lare che la riga inizi * effettivamente per '#', e ritornare un error e in caso contrario. */ fgets(temp, 80, f_med); }

fclose(f_osp); fclose(f_med); }

Se avessi voluto evitare di avere ripetizioni di istituti ospedalieri nel file di output mi sarei potuto appoggiare sulla seguente procedura ausiliaria istituto_presente , che riceve in ingresso il descrittore di un file binario (gia' aperto sia in lettura che in scrittuta) contente i dati di istituti ospedalieri, ed il nome di un istituto ospedaliero, e ritorna true se nel file esiste un record corrispondente al nome passato. Si noti che, se l'istituto non viene trovato, il cursore del file si trova in fondo al file stesso, altrimenti si trova sull'elemento appena successivo a quello con nome uguale a quello passato come parametro. bool istituto_presente(FILE *file, char nome_osp[]){ IstitutoOspedaliero ist; rewind(file); while(!feof(file)){ fread(&ist, sizeof(IstitutoOspedaliero), 1, file); if(strcmp(ist.nome, nome_osp) == 0) return true; } return false; }

Avendo a disposizione la procedura istituto_presente , al posto della semplice fwrite , nel corpo della procedura scrivi_ospedali va sostituito il seguente pezzo di codice:

if(!istituto_presente(f_osp, ist.nome)) { fwrite(&ist, sizeof(IstitutoOspedaliero), 1, f_osp); }

Si noti inoltre che il file "istitutiOspedalieri" in questo caso non puo' essere piu' aperto solo in scrittura, ma anche in lettura, quindi la chiamata alla fopen di "istitutiOspedalieri" va modificata sostituendo al posto della modalita' di apertura "wb" la modalita' "wb+" come illustrato di seguito:

f_osp = fopen("istitutiOspedalieri", "wb+");

Esercizio 24

Si considerino le seguenti dichiarazioni:

typedef int *tipo1;

int x, y;

int *a;

tipo1 b;

Si dica, per ognuna delle seguenti sequenze di istruzioni C, se essa è corretta o no; in caso positivo si dica qual è il risultato prodotto; in caso negativo si dica in che cosa consiste l’errore e se si tratta di errore a compile-time o a run-time.

Sequenza 1:

x = 0; y = 1; a = NULL; b = x; a = b; printf (“il valore di x e y è, rispettivamente, %d, %d, \n”, x, y);

Sequenza 2:

scanf (“%d”, &y); a = NULL; x = 0;

if (y > 0) {b = a; x = *b + y; }

printf (“il valore di x e y è, rispettivamente, %d, %d, \n”, x, y);

Sequenza 3:

a = &y; x = 2; y = 5; b = a; a = NULL; *b = y + x;

printf (“il valore di x e y è, rispettivamente, %d, %d, \n”, x, y);

Soluzione

Sequenza 1: è scorretta in quanto l'assegnamento b=x non ha senso (b è un puntatore ad intero, x è un intero. Questo è un errore a compile-time (lo riconosce il compilatore).

Sequenza 2: è scorretta in quanto, se il valore y immesso dall'utente è > 0, prima viene eseguito l'assegnamento b=a (in cui b va a puntare a NULL, in quanto a era stato inizializzato a NULL), e poi si cerca di dereferenziare b (*b), che però non è ammissibile in quanto b punta a NULL. Questo è un errore a run-time (lo si trova solo facendo girare il programma e fornendo in ingresso un valore di y > 0).

Sequenza 3: è corretta, e stampa 2 per x e 7 per y. Infatti, prima dell'istruzione *b=y+x si ha una situazione in cui x vale 2, y vale 5, b punta a y, a punta a NULL. Quindi l'assegnamento mette il valore 7 (5+2) nella variabile puntata da b, che è y.

Esercizio 25

Si scriva un programma che:

• Legge dallo StandardInput due stringhe di caratteri (lunghe al massimo 100 caratteri l'una) separate tra loro dal carattere speciale (non appartenente alle stringhe) ‘#’ e terminate dallo stesso carattere '#' (si supponga pure che l'utente non immetta mai stringhe più lunghe di 100 caratteri).

• Verifica che le lunghezze delle due stringhe (senza contare il carattere '#') differiscano al massimo di un’unità. In caso contrario interrompa l’esecuzione scrivendo sullo StandardOutput un opportuno messaggio.

• Scriva sullo StandardOutput una stringa ottenuta alternando i caratteri delle due stringhe di ingresso (un carattere di una stringa seguito da un carattere dell’altra mantenendo l’ordine originario tra i caratteri della stessa stringa) secondo la regola seguente:

o Se una delle due stringhe è più lunga dell’altra si deve cominciare con il primo carattere della stringa più lunga.

o Se le due stringhe sono di lunghezza uguale, si può cominciare con una qualsiasi delle due.

o Facoltativamente, se la stringa risultante è più lunga di 80 caratteri, ogni 80 caratteri scritti si deve andare a capo.

Soluzione #include <stdio.h> #define MAX_L 100 main () { char s1[MAX_L], s2[MAX_L], curr; int n_el1=0, n_el2=0; int i; do{ scanf("%c", &curr); if (curr != '#'){ s1[n_el1] = curr; n_el1++; } } while(curr != '#'); do{ scanf("%c", &curr); if(curr != '#'){ s1[n_el2] = curr; n_el2++; } } while(curr != '#'); if(n_el1-n_el2 <= 1 && n_el1-n_el2 >= -1){ /* caso in cui la stringa 1 è più lunga della s tringa 2 */ if(n_el1 > n_el2){ for (i=0; i<n_el2; i++){ printf("%c%c", s1[i], s2[i]); if(((i+1)*2) % 80 == 0) printf("\n"); } /* chiude il for */ printf("%c, s1[n_el1-1]) } else { /*n_el2 >= n_el1*/ for (i=0; i<n_el1; i++){ printf("%c%c", s2[i], s1[i]); if(((i+1)*2) % 80 == 0) printf ("\n"); } /* chiude il for */ /*se le due stringhe sono di lunghezza divers a*/ if(n_el2 > n_el1) printf ("%c, s2[n_el2-1]); } /* chiude il ramo else dell’if precedente*/ } else { printf ("Le lunghezze delle stringhe differisco no tra loro di più di una unità"); } }

Esercizio 26

Si scriva un programma che, non facendo uso di accesso diretto a file (ossia usando esclusivamente le funzioni di libreria fopen, fclose, fread, fwrite e rewind), dato un file di interi "InputInteri" produca un nuovo file "OutputInteri" contenente gli stessi elementi del file

di ingresso ma scritti in ordine inverso. Si assuma che la memoria centrale non sia sufficientemente grande da contenere l’intero file ma possa contenerne circa i 2/3.

NB:

1. Tra le varie possibili soluzioni saranno valutate meglio quelle più efficienti.

2. E’ auspicata una soluzione che faccia uso di opportuni sottoprogrammi. Alcuni di essi, eventualmente, potrebbero essere definiti anche solo parzialmente mediante pseudocodice, evitando una codifica completa.

3. Eventualmente si può assumere l’ipotesi semplificativa che la dimensione del file sia nota a priori e valga la costante K. In tal caso il punteggio massimo dell’esercizio viene diminuito di 1 punto.

Soluzione main () { /* elInFile calcola il numero di interi nel file binario f * (supposto gia' aperto in sola lettura). */ long int elInFile(FILE *f); /* readArrayFromFile prende in ingresso il descri ttore di un file binario * (supposto aperto lettura) ed un array di inter i, e legge n_el * interi dal file, memorizzandoli * nell'array (supposto grande a sufficienza per contenere i dati). */ void readArrayFromFile(FILE *f, int *ar, long int n_el); /* reverseArrayInFile prende in ingresso il descr ittore di un file * binario (supposto vuoto e aperto in sola scrit tura) ed un array di * interi di lunghezza n_el e scrive il contenuto dell'array nel file, * partendo dall'ultimo elemento dell'array fino al primo. */ void reverseArrayInFile(FILE *f, int *ar, long int n_el); /* appendFile prende in ingresso i descrittori di due file binari * fromFile ed inFile, il primo aperto in lettura , il secondo in * scrittura, e scrive gli elementi del primo fil e in fondo al secondo, * mantenendo l'ordine in cui essi compaiono nel primo. */ void appendFile(FILE *fromFile, FILE *inFile); FILE *in, *out, *tempFile; int *tempAr; long int tot_n_el, n_el; in = fopen("InputInteri", "rb"); out = fopen("OutputInteri", "wb"); tot_n_el = elInFile(in); n_el = tot_n_el/2 + 1; tempAr = malloc( sizeof(int)*n_el); rewind(in); readArrayFromFile(in, tempAr, n_el); tempFile = fopen("_tf", "wb+"); reverseArrayInFile(tempFile, tempAr, n_el); n_el = tot_n_el - n_el; readArrayFromFile(in, tempAr, n_el); reverseArrayInFile(out, tempAr, n_el); appendFile(tempFile, out);

fclose(in); fclose(out); fclose(temp); } long int elInFile(FILE *f){ int temp; long int res = 0; rewind(f); while(fread(&temp, sizeof(int), 1, f) == 1){ res++; } return res; } void readArrayFromFile(FILE *f, int *ar, long int n_el){ long int i; for(i=0 ; i<n_el ; i++){ fread(&ar[i], sizeof(int), 1, f); } /* oppure, in alternativa, si poteva risolvere tu tto con una * istruzione singola: * fread(ar, sizeof(int), n_el, f); */ } void reverseArrayInFile(FILE *f, int *ar, long int n_el){ long int i; for(i=n_el-1 ; i>=0 ; i--){ fwrite(&ar[i], sizeof(int), 1, f); } } void appendFile(FILE *fromFile, FILE *inFile){ int temp; rewind(fromFile); while(fread(&temp, sizeof(int), 1, fromFile) == 1){ fwrite(&temp, sizeof(int), 1, inFile); } }

Esercizio 27

Parte a.

Definire dei tipi di dato per contenere informazioni relative a patenti e multe.

Una patente è definita da un numero identificativo, dal nome del proprietario, dal suo codice fiscale, da una data di rilascio e una di scadenza, e da un numero di punti ancora disponibili sulla patente.

Una multa è definita da un numero identificativo, dal numero identificativo della patente cui viene addebitata, dalla data in cui è stata emessa, dall'ammontare della multa (in euro), dal numero di punti da togliere alla patente a causa della multa.

Parte b.

Codificare un sottoprogramma che prende in ingresso i descrittori fpat e fmul di 2 file (entrambi supposti aperti sia in lettura che in scrittura), uno contenente una serie di patenti, ed uno contenente una serie di multe, e ritorna il descrittore di un nuovo file.

Il sottoprogramma deve realizzare quanto segue: per ogni multa presente nel file fmul , il sottoprogramma modifica i dati della patente corrispondente nel file fpat , togliendo i punti indicati nella multa.

Se una patente, dopo avere sottratto i punti di una multa, ha un residuo di punti minore o uguale a zero, il sottoprogramma scrive in un terzo file (il cui descrittore verrà ritornato alla fine) la posizione nel file fpat della suddetta patente.

Si assuma che nel file fpat le patenti siano elencate in ordine di data di scadenza, mentre nel file fmul le multe siano ordinate secondo l’identificatore della patente a cui si riferiscono.

Si supponga pure che tutte le multe del file fmul si riferiscano a patenti effettivamente esistenti nel file fpat .

Versione semplificata

Si assuma che per ogni patente nel file fmul si trovi al massimo una multa.

Soluzione parte a typedef struct { unsigned long id; char nome[80]; char CF[16]; TipoData rilascio; TipoData scadenza; int punti; } TipoPatente; /* dove TipoData è stata più volte dichiarata a lez ione e * esercitazione. */ typedef struct { unsigned long id; unsigned long id_pat; TipoData emessa; float somma; int punti; } TipoMulta;

Soluzione parte b

Versione completa. FILE *aggiornaPatenti(FILE *fpat, FILE *fmul){ TipoMulta m_cor, m_prec; unsigned long id_pat; int totPunti; long pos_pat; bool next_pat; /* bool è definito come al solito */ FILE *fsottozero = fopen("invalide.txt", "wb"); rewind(fmul); while(!feof(fmul)){ next_pat = false; totPunti = 0; do { fread(&m_cor, sizeof(TipoMulta), 1, fmul); if(totPunti == 0){ /* è la prima multa che leggo relativa alla prossima patente */ totPunti = m_cor.punti; m_prec = m_cor; } else if (m_cor.id_pat == m_prec.id_pat){ /* la multa appena letta si riferisce alla stessa patente della

* multa precedente, mi, limito a sommare i punti da togliere */ totPunti += m_cor.punti; } else { /* devo passare alla prossima multa; come p rima cosa, * ritorno sulla multa precedente */ fseek(fmul, -( sizeof(TipoMulta)), SEEK_CUR); m_cor = m_prec; next_pat = true; } } while(!feof(fmul) && next_pat == false); /* Vado a cercare la patente da modificare nel file di patenti, * quindi faccio i cambiamenti necessari; */ rewind(fpat); do { fread(&p, sizeof(TipoPatente), 1, fpat); } while(p.id != m_cor.id_pat); fseek(fpat, -( sizeof(TipoPatente)), SEEK_CUR); p.punti -= totPunti; pos_pat = ftell(fpat); fwrite(&p, sizeof(TipoPatente), 1, fpat); /* se la patente ha un numero di punti <= 0, * la segnalo nel file apposito; */ if (p.punti <= 0){ fwrite(&pos_pat, sizeof(long), 1, fsottozero); } } /* fine del while esterno */ return fsottozero; }

Versione semplificata. FILE *aggiornaPatenti(FILE *fpat, FILE *fmul){ TipoMulta m_cor, m_prec; unsigned long id_pat; int totPunti; long pos_pat; bool next_pat; /* bool è definito come al solito */ FILE *fsottozero = fopen("invalide.txt", "wb"); rewind(fmul); while(fread(&m_cor, sizeof(TipoMulta), 1, fmul) == 1){ /* Vado a cercare la patente da modificare nel file di patenti, * quindi faccio i cambiamenti necessari; */ rewind(fpat); do { fread(&p, sizeof(TipoPatente), 1, fpat); } while(p.id != m_cor.id_pat); fseek(fpat, -( sizeof(TipoPatente)), SEEK_CUR); p.punti -= m_cor.punti; pos_pat = ftell(fpat); fwrite(&p, sizeof(TipoPatente), 1, fpat); /* se la patente ha un numero di punti <= 0, * la segnalo nel file apposito; */ if (p.punti <= 0){ fwrite(&pos_pat, sizeof(long), 1, fsottozero); } } /* fine del while esterno */ return fsottozero; }

Esercizio 28

Parte a.

Definire dei tipi di dato per contenere informazioni relative ad un albero genealogico.

Ogni persona è caratterizzata dai suoi dati anagrafici e dai suoi parenti più stretti.

I dati anagrafici di una persona sono il suo codice fiscale (che è una stringa alfanumerica di esattamente 16 cifre), nome e cognome, il luogo e la data di nascita, il suo sesso e lo stato matrimoniale (single o sposato).

Inoltre, ad ogni persona sono associati le seguenti informazioni: il codice fiscale dello sposo/sposa (naturalmente se la persona è sposata); i codici fiscali dei genitori; il codici fiscali dei figli (si assuma pure che una coppia non possa avere più di 10 figli). Se qualcuna di queste informazioni non è disponibile, il cofice fiscale corrispondente contiene solo '0' (per esempio se la persona non è sposata, il codice fiscale associato allo sposo/a è 0000000000000000).

Parte b.

Codificare un sottoprogramma che prende in ingresso un codice fiscale e le informazioni di una persona (dati anagrafici e parenti più stretti), e ritorna true se la persona passata ha il codice fiscale corrispondente al primo parametro.

Parte c.

Codificare un sottoprogramma che prende in ingresso il descrittore di un file binario (supposto già aperto in sola lettura) contenente le informazioni di un insieme di persone, il quale stampa a video i nomi delle coppie di persone sposate, indicando prima il marito, e poi la moglie (deve cioè stampare, su ogni riga, i nomi di una coppia marito-moglie, indicando prima il marito).

NB: Ogni coppia va stampata una volta sola. L'ordine con cui vengono stampate le coppie può essere qualunque. Il file in ingresso può anch'esso avere le persone in ordine qualunque.

Il sottoprogramma deve essere side-effect-free, cioè quando termina esso deve lasciare il file in ingresso ESATTAMENTE nello stesso stato in cui questo era all'inizio del sottoprogramma.

Eventuali sottoprogrammi di appoggio (che non facciano parte della standard library del C) devono essere codificati nella loro interezza.

Soluzione parte a

typedef TipoCF char[16];

typedef TipoSesso enum{M, F};

typedef struct { TipoCF cf; char nome[30]; char cognome[30]; char luogoNascita[30]; TipoData dataNascita; TipoSesso s; bool sposato;

} TipoDatiAnagrafici

typedef struct { TipoDatiPersona anagrafica; TipoCF sposo; TipoCF padre; TipoCF madre; TipoCF figli[10]; int nfigli; } TipoDatiPersona

Con i seguenti classici tipi:

typedef enum{false, true} bool;

typedef struct{ short giorno; short mese; int anno; } TipoData;

Soluzione parte b

bool confrontaCF(TipoCF cf, TipoDatiPersona pers){ int i; for(i=0; i<16; i++){ if(cf[i] != pers.anagrafica.cf[i]) return false; } return true; }

Soluzione parte c

include #stdio.h;

void stampaSposi(FILE *f;){ bool trovaPersonaInFile(TipoCF cf, FILE *f, TipoDatiPersona *dpOut); long oldPos = ftell(f); TipoCF cfSposo; TipoDatiPersona dp, sposo; TipoDatiPersona *marito, *moglie; rewind(f); while(!feof(f)){ fread(&dp, sizeof(TipoDati(Persona), 1, f); if(dp.anagrafica.sposato){ if(trovaPersonaInFile(marito.anagrafica.cf, f, &sposo ,)){ if(dp.anagrafica.sesso == M){ marito = &dp; moglie = &sposo; } else { marito = &sposo; moglie = &dp; }

printf("%s %s, %s %s\n", marito->anagrafica .nome, marito->anagrafica .cognome, moglie->anagrafica .nome, moglie->anagrafica .cognome); } } } fseek(f, posCorr, SEEK_SET); } /* Funzione che prende in ingresso il codice fiscal e di una persona * ed un file in cui cercarla, e ritorna true se la persona * esiste nella porzione di file seguente al punto in cui si trova * il puntatore nel file al momento della chiamata, altrimenti * ritorna false (nel caso cioè in cui o la persona non esiste, * oppure, se esiste, si trova nel file in un punto precedente). * Se la persona esiste, i suoi dati sono caricati nella variabile * dp passata per indirizzo. * La funzione lascia il puntatore nel file nel pun to in cui lo * aveva trovato all'inizio dell'esecuzione. */ bool trovaPersonaInFile(TipoCF cf, FILE *f, TipoDatiPersona *dpOut){ long posCorr = ftell(f); TipoDatiPersona dp; while(!feof(f)){ fread(&dp, sizeof(TipoDati(Persona), 1, f); if(confrontaCF(cf, dp) == true){ *dpOut = dp; fseek(f, posCorr, SEEK_SET); return true; } } fseek(f, posCorr, SEEK_SET); return false; }

Esercizio 29

Parte a.

Definire dei tipi di dato per contenere informazioni relative alle misurazioni rilevate dai sensori del sistema di monitoraggio di un impianto industriale.

Ogni sensore rileva una misurazione che può essere o di temperatura, o di pressione, o di voltaggio, ed è caratterizzato da un numero identificativo (un intero maggiore o uguale a 1), da un codice di 3 lettere che ne identifica la posizione, e da un valore reale che indica la precisione del sensore. Ogni sensore ha un numero identificativo unico (non ci possono essere 2 sensori con lo stesso identificativo), mentre più sensori possono essere messi nella stessa posizione.

Una misurazione è fatta di: codice identificativo del sensore che ha effettuato la rilevazione; valore del dato letto, data della rilevazione, ora, minuti e secondi della rilevazione.

Parte b.

Codificare un sottoprogramma che prende in ingresso una misurazione m ed il descrittore f di un file binario (supposto già aperto sia in lettura che in scrittura) contenente misurazioni ordinate in ordine di data/ora crescente, ed inserisce m in f, avendo cura di mantenere l'ordinamento.

NB: Eventuali sottoprogrammi di appoggio (che non facciano parte della standard library del C) devono essere codificati nella loro interezza.

Parte c.

Codificare un sottoprogramma che prende in ingresso:

� il descrittore di un file binario (supposto già aperto in sola lettura) contenente le informazioni relative ai sensori dell'impianto;

� il descrittore di un file binario (supposto già aperto in sola lettura) contenente tutte le misurazioni effettuate da tutti i sensori dell'impianto, ordinate in ordine di data/ora crescente;

� un codice di 3 lettere indicante una posizione nell'impianto.

Il sottoprogramma stampa a video l'ultimo tra i dati (cioè il dato con data/ora maggiore) rilevati da sensori che si trovano nella posizione specificata come terzo parametro.

NB: Eventuali sottoprogrammi di appoggio (che non facciano parte della standard library del C) devono essere codificati nella loro interezza.

Nel file binario con i dati dei sensori, ogni sensore compare una volta sola; i sensori nel file non sono ordinati secondo nessun ordine particolare.

Soluzione parte a

typedef TipoPos char[3];

typedef TipoSensore enum{T, P, V};

typedef struct { int id; TipoSensore tipo; TipoPos pos; double prec; } TipoSensore;

typedef struct { int idSensore; double mis; TipoDataOra dataora; } TipoMisurazione;

Con i seguenti tipi aggiuntivi:

typedef struct{ short giorno; short mese; int anno;

} TipoData;

typedef struct{ short ora; short minuti; short secondi; } TipoOra;

typedef struct{ TipoData d; TipoOra o; } TipoDataOra;

Soluzione parte b

include #stdio.h;

void aggiungiMis(FILE *f, TipoMisurazione m){ int dataOraCmp(TipoDataOra d1, TipoDataOra d2); TipoMisurazione tm; long pos; FILE *tmp; bool added = false; rewind(f); while(!feof(f) && !added){ fread(&tm, sizeof(TipoMisurazione), 1, f); if(dataOraCmp(m.dataora, tm.dataora)<=0){ fseek(f, - sizeof(TipoMisurazione), SEEK_CUR); pos = ftell(f); tmp = fopen("_tmp", wb+); while(!feof(f)){ fread(&tm, sizeof(TipoMisurazione), 1, f); fwrite(&tm, sizeof(TipoMisurazione), 1, tmp); } fseek(f, pos, SEEK_SET); fwrite(&m, sizeof(TipoMisurazione), 1, f); while(!feof(tmp)){ fread(&tm, sizeof(TipoMisurazione), 1, tmp); fwrite(&tm, sizeof(TipoMisurazione), 1, f); } added = true; } } if(!added){ fwrite(&m, sizeof(TipoMisurazione), 1, f); } }

in cui il tipo bool è definito come al solito e la funzione dataOraCmp ritorna -1, 0, 1 a seconda che d1 sia <, = o > di d2, ed è definita nel seguente modo:

int dataOraCmp(TipoDataOra d1, TipoDataOra d2){ if (d1.d.anno < d2.d.anno) return -1; else if (d1.d.anno > d2.d.anno) return 1; if (d1.d.mese < d2.d.mese) return -1; else if (d1.d.mese > d2.d.mese) return 1;

if (d1.d.giorno < d2.d.giorno) return -1; else if (d1.d.giorno > d2.d.giorno) return 1; if (d1.o.ora < d2.o.ora) return -1; else if (d1.o.ora > d2.o.ora) return 1; if (d1.o.minuti < d2.o.minuti) return -1; else if (d1.o.minuti > d2.o.minuti) return 1; if (d1.o.secondi < d2.o.secondi) return -1; else if (d1.o.secondi > d2.o.secondi) return 1; return 0; }

Soluzione parte c

include #stdio.h;

void stampaUltimo(FILE *sens, FILE *mis, TipoPos p){ TipoSensore trovaSensoreInFile(int sensID, FILE *f); long pos; TipoMisurazione tm; TipoSensore ts; fseek(mis, 0, SEEK_END); pos = ftell(mis); while(pos>0){ fseek(mis, - sizeof(TipoMisurazione), SEEK_CUR); fread(&tm, sizeof(TipoMisurazione), 1, mis); ts = trovaSensoreInFile(tm.idSensore, sens) ; if(p[0]==ts.pos[0] && p[1]==ts.pos[1] && p[2]==ts.pos [2]){ printf("%d: %lf (%d/%d/%d %d:%d:%d)\n", tm.idSensore, tm.mis, tm.dataora.d.giorno, tm.dataora.d.mese , tm.dataora.d.anno, tm.dataora.o.ora, tm.dataora.o.minuti, tm.dataora.o.secondi); return; } fseek(mis, - sizeof(TipoMisurazione), SEEK_CUR); pos = ftell(mis); } } /* Funzione che prende in ingresso il codice identi ficativo di un * sensore ed un file con dati di sensori, e ritorn a la descrizione * completa del sensore. * Si suppone che il sensore esista nel file, per c ui non viene * prevista alcuna condizione di errore. */ TipoSensore trovaSensoreInFile( int sensID, FILE *f){ TipoSensore ts; rewind(f); while(!feof(f)){ fread(&ts, sizeof(TipoSensore), 1, f); if(ts.id == sensID) return ts; } }