Informatica: arte e mestiere

Dimensione: px
Iniziare la visualizzazioe della pagina:

Download "Informatica: arte e mestiere"

Transcript

1 Informatica: arte e mestiere III edizione su Web

2 Premessa...3 Capitolo 3 Codifica degli algoritmi in un linguaggio di alto livello...4 Capitolo 4 Esecuzione di programmi C su macchine reali...8 Capitolo 5 Tipi di dato...13 Capitolo 6 Strutture di controllo...18 Capitolo 7 Funzioni e procedure...27 Capitolo 8 Introduzione alla programmazione ricorsiva...32 Capitolo 9 Gestione dei file...35 Capitolo 10 Strutture dati dinamiche...43 Capitolo 14 Archivi e basi di dati...57 Capitolo 18 La visione dei sistemi informatici da parte dell utente finale...59 Copyright The McGraw-Hill Companies srl 2

3 Premessa Il presente file contiene numerosi esercizi utilizzabili dai docenti che adottano il testo Informatica: arte e mestiere (II edizione) come materiale didattico ausiliario. Gli esercizi contenuti nel file non costituiscono un eserciziario; pertanto non possono e non devono essere utilizzati direttamente dagli studenti dei corsi, ma sono proposti ai docenti come spunto per completare e verificare l apprendimento durante lo svolgimento del corso e in sede d esame. Di conseguenza, il livello di difficoltà e complessità è fortemente variabile da esercizio a esercizio; inoltre le soluzioni sono fornite in forma spesso assai schematica. Gli esercizi sono suddivisi per capitoli a seconda della pertinenza. Ovviamente, la loro numerosità e complessità varia anche in funzione del capitolo cui fanno riferimento. In alcuni casi essi sono del tutto assenti. È intenzione degli autori aggiornare periodicamente gli esercizi proposti, anche in funzione di eventuali richieste e suggerimenti, sempre benvenuti. Copyright The McGraw-Hill Companies srl 3

4 Capitolo 3 Codifica degli algoritmi in un linguaggio di alto livello Il nucleo del linguaggio C: input/output semplificato, variabili, istruzioni condizionali e cicli, vettori. o 3.1 Si descriva un algoritmo, utilizzando il nucleo del linguaggio C descritto nel capitolo 3, che legga dallo standard input due numeri interi positivi e stampi in uscita un messaggio che indichi se i due numeri sono primi tra loro oppure no. Soluzione dell esercizio 3.1 Due numeri m e n sono primi tra loro se e solo se vale che MCD(m, n) = 1. Il problema si riduce quindi a trovare il massimo comun divisore tra m e n e verificarne il valore. Utilizziamo l algoritmo di Euclide descritto nell Esempio 3.7. main() scanf (m); scanf (n); while (m!= n) if (m > n) m = m - n; n = n - m; mcd = n; /* Si utilizza l algoritmo di Euclide per calcolare l MCD */ /* A questo punto si aggiungono le istruzioni che valutano l MCD */ if (mcd == 1) printf ("I due numeri sono primi tra loro"); printf ("I due numeri non sono primi tra loro"); o 3.2 Si vuole memorizzare in forma compatta un elenco di parole ordinato alfabeticamente. Allo scopo si osserva che spesso due parole consecutive hanno una parte iniziale comune. Ogni parola che ha una parte iniziale comune con la precedente viene quindi rappresentata con una cifra decimale compresa tra 2 e 9 indicante un numero di lettere uguali a quelle nella parole precedente, seguita dalle altre lettere della parola. L assenza di cifra all inizio della parola ha lo stesso significato della cifra 0. Ogni parola è separata dalla successiva da uno o più spazi bianchi. Esempio: abaco 3te 2bandonare 9to 2normale ammiraglio 6re bitume 3orzolo 8uto Scrivere un programma, utilizzando il nucleo del linguaggio C descritto nel capitolo 3, che esegue lo scompattamento di tale rappresentazione, cioè legge dallo standard input una sequenza di caratteri che rappresenta la codifica abbreviata del testo (si assuma che la sequenza sia terminata dal carattere speciale # ) e la riscrive nello Copyright The McGraw-Hill Companies srl 4

5 standard output togliendo le cifre e aggiungendo i caratteri necessari in modo da rappresentare tutte le parole per esteso. Esempio, dalla sequenza sopra riportata si ottenga la seguente: abaco abate abbandonare abbandonato abnormale ammiraglio ammirare bitume bitorzolo bitorzoluto Si scriva poi un programma che esegue l operazione inversa. Soluzione dell esercizio 3.2 main() ContatoreTestoCompatto = 0; scanf (carattere); while (carattere!= '#') TestoCompatto[ContatoreTestoCompatto] = carattere; ContatoreTestoCompatto = ContatoreTestoCompatto + 1; scanf (carattere); LunghTestoCompatto = ContatoreTestoCompatto; /* Fase di inserimento del testo compatto */ /* Fase di scompattamento del testo */ ContatoreTestoCompatto = 0; ContatoreTestoNormale = 0; CaratterePrecedente = ' '; while (ContatoreTestoCompatto < LunghTestoCompatto) carattere = TestoCompatto[ContatoreTestoCompatto]; /* Se il carattere non è un numero, copialo nel vettore del testo normale */ if (carattere < '0' carattere> '9') TestoNormale[ContatoreTestoNormale] = carattere; /* Tiene traccia dell'inizio dell'ultima parola nel testo normale */ if (CaratterePrecedente == ' ' && carattere!= ' ') ContatoreInizioParola = ContatoreTestoNormale; ContatoreTestoNormale = ContatoreTestoNormale + 1; /* Se è un numero, copia caratteri dall'ultima parola inserita nel testo normale */ NumLettereUguali = TestoCompatto[ContatoreTestoCompatto] - '0'; for (i = 0; i < NumLettereUguali; i++) TestoNormale[ContatoreTestoNormale + i] = TestoNormale[ContatoreInizioParola + i]; ContatoreInizioParola = ContatoreTestoNormale; ContatoreTestoNormale = ContatoreTestoNormale + NumLettereUguali; ContatoreTestoCompatto = ContatoreTestoCompatto + 1; CaratterePrecedente = carattere; for (i = 0; i < ContatoreTestoNormale; i++) /* Fase di stampa del testo scompattato */ Copyright The McGraw-Hill Companies srl 5

6 printf (TestoNormale[i]); Adesso scriviamo il programma che eseugue l operazione inversa: genera il testo compattato a partire da quello esteso. main() /* Fase di inserimento del testo normale */ ContatoreTestoNormale = 0; scanf (carattere); while (carattere!= '#') TestoNormale[ContatoreTestoNormale] = carattere; ContatoreTestoNormale = ContatoreTestoNormale + 1; scanf (carattere); LunghTestoNormale = ContatoreTestoNormale; /* Fase di compattamento del testo */ ContatoreTestoCompatto = 0; ContatoreTestoNormale = 0; ContatoreInizioParola = -1; ContatoreLettere = 0; ContatoreInizioParolaPrec = 0; CaratterePrecedente = ' '; while (ContatoreTestoNormale < LunghTestoNormale) carattere = TestoNormale[ContatoreTestoNormale]; /* Tiene traccia dell'inizio della parola precedente */ /* nel testo normale */ if (CaratterePrecedente == ' ' && carattere!= ' ') ContatoreInizioParolaPrec = ContatoreInizioParola; ContatoreInizioParola = ContatoreTestoNormale; /* Controlla le lettere della parola corrente */ controlla = 1; /* Se il carattere è presente nella corrispondente posizione */ /* della parola precedente */ if (controlla == 1 && carattere == TestoNormale[ContatoreInizioParolaPrec + ContatoreLettere]) ContatoreLettere = ContatoreLettere + 1; ContatoreTestoNormale = ContatoreTestoNormale + 1; /* Se il carattere non è presente nella corrispondente posizione */ /* della parola precedente*/ /* Non controllare più per la parola corrente*/ controlla = 0; /* Se vi erano lettere uguali, scrivi il contatore */ if (ContatoreLettere > 0) TestoCompatto[ContatoreTestoCompatto] = ContatoreLettere + '0'; ContatoreLettere = 0; /* Altrimenti, scrivi il carattere */ Copyright The McGraw-Hill Companies srl 6

7 TestoCompatto[ContatoreTestoCompatto] = carattere; ContatoreTestoNormale = ContatoreTestoNormale + 1; ContatoreTestoCompatto = ContatoreTestoCompatto + 1; CaratterePrecedente = carattere; for (i = 0; i < ContatoreTestoCompatto; i++) printf (TestoCompatto[i]); /* Fase di stampa del testo compattato */ Copyright The McGraw-Hill Companies srl 7

8 Capitolo 4 Esecuzione di programmi C su macchine reali Il C completo: dichiarazione delle variabili, stringhe di formato, direttiva #include. o 4.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: Inserisci numero: Inserisci numero: 4 Inserisci numero: Il rapporto (o il suo inverso) tra -2.5 e vale 6.25 Soluzione dell esercizio 4.1 #include <stdio.h> main() float dati[100], sum, rapp, inv_rapp; int i, j, n_dati; unsigned int trovata; sum = 0; n_dati = 0; do printf("inserisci numero: "); scanf("%f", &dati[n_dati]); sum += dati[n_dati]; n_dati++; while (sum <= 50 && n_dati < 50); trovata = 0; if (n_dati >= 3) i = 1; while (trovata == 0 && i < n_dati - 1) j = i+1; while (trovata == 0 && j < n_dati) if (dati[i] == 0 dati[j] == 0) Copyright The McGraw-Hill Companies srl 8

9 i++; if (dati[0] == 0) trovata = 1; rapp = dati[i]/dati[j]; inv_rapp = dati[j]/dati[i]; if (rapp - dati[0] < && rapp - dati[0] > inv_rapp - dati[0] < && inv_rapp - dati[0] > ) trovata = 1; if (trovata == 1) printf( Il rapporto (o il suo inverso) tra %f e %f vale %f\n, dati[i], dati[j], dati[0]); j++; printf( Sono stati inseriti solo %d elementi\n, n_dati); o 4.2 Scrivere un programma C che esegua la seguente funzionalità. Si legge da tastiera una sequenza di n interi positivi (massimo 100 numeri; la sequenza termina quando l'utente inserisce il numero 0); dopo avere letto tutti i numeri, per ogni numero letto viene stampato, ogni volta su una riga diversa e da sinistra verso destra, un numero di asterischi pari al numero in questione. Esempio di funzionamento del programma: Inserisci numero (0 per terminare): 3 Inserisci numero (0 per terminare): 6 Inserisci numero (0 per terminare): 3 Inserisci numero (0 per terminare): 5 Inserisci numero (0 per terminare): 0 *** ****** *** ***** Soluzione esercizio 4.2 #include <stdio.h> main() unsigned int dati[100], dato_corr; int i, j, n_dati; n_dati = 0; do printf("inserisci numero (0 per terminare): "); scanf("%u", &dato_corr); Copyright The McGraw-Hill Companies srl 9

10 if (dato_corr!= 0) dati[n_dati] = dato_corr; n_dati++; while (dato_corr!= 0 && n_dati < 100); for( i=0 ; i<n_dati ; i++ ) for( j=0 ; j<dati[i] ; j++ ) printf("*"); printf("\n"); o 4.3 Scrivere un programma che legge da tastiera una sequenza di numeri interi e: a) verifica che siano tutti non negativi (nel caso si trovi un numero negativo, il programma dovrà terminare); b) stampa a video: a. la media dei numeri pari; b. la media dei numeri dispari; c. la media di tutti i numeri. La sequenza sarà composta al massimo da 100 numeri. Soluzione esercizio 4.3 #include <stdio.h> main() unsigned int i, num_dati; int dati[100]; unsigned int negativo; int media, somma, media_pari, somma_pari, media_dispari, somma_dispari; int cont_pari, cont_dispari; /* Lettura dei dati; massimo 100 dati*/ do printf("quanti numeri vuoi inserire?: "); scanf("%u", &num_dati); while (num_dati == 0 num_dati > 100); for (i = 0; i < num_dati; i++) printf("inserisci numero (%u/%u): ", i + 1, num_dati); scanf("%d", &dati[i]); /* Controlla se i dati sono tutti non negativi */ negativo = 0; i = 0; while (i < num_dati && negativo == 0) if (dati[i] < 0) negativo = 1; i = i + 1; Copyright The McGraw-Hill Companies srl 10

11 if (negativo == 0) somma = 0; somma_pari = 0; somma_dispari = 0; cont_pari = 0; cont_dispari = 0; for (i = 0; i < num_dati; i++) somma = somma + dati[i]; /* Se i dati sono tutti non negativi, continua con l'elaborazione */ if (dati[i] % 2 == 0) somma_pari = somma_pari + dati[i]; cont_pari = cont_pari + 1; somma_dispari = somma_dispari + dati[i]; cont_dispari = cont_dispari + 1; media = somma / num_dati; media_pari = somma_pari / cont_pari; media_dispari = somma_dispari / cont_dispari; printf ("Media totale: %d\n", media); printf ("Media numeri pari: %d\n", media_pari); printf ("Media numero dispari: %d\n", media_dispari); /* Calcola medie */ /* L'operatore 'percentuale' calcola il resto */ /* della divisione intera, detto 'modulo' */ /* Se c'è almeno un numero negativo, termina l esecuzione */ printf ("I numero devono essere tutti non negativi\n"); o 4.4 Si scriva un programma che calcola la media, il minimo e il massimo di una sequenza di numeri reali letti dallo standard input; la sequenza viene terminata dal numero. I valori risultanti devono essere scritti sullo standard output. Soluzione dell'esercizio 4.4 #include <stdio.h> main() float x, s, min, max; unsigned int n, num_dati; printf("quanti numeri vuoi inserire?: "); scanf("%u", &num_dati); s = 0.0; for (n = 0; n < num_dati; n++) printf ("Numero: "); Copyright The McGraw-Hill Companies srl 11

12 scanf ( %f, &x); s = s + x; if (n == 0) min = x; max = x; if (x < min) min = x; if (x > max) max = x; if (n == 0) printf ("nessun numero inserito"); printf ("La media è %f \n il minimo è %f \n il massimo è %f \n", s / n, min, max); Copyright The McGraw-Hill Companies srl 12

13 Capitolo 5 Tipi di dato Inizializzazione delle variabili, definizione di nuovi tipi, puntatori, direttiva #define. o 5.1 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 di tipo Giocattolo. Si dichiarino successivamente due variabili, Giocattoli e Bambini, consistenti in due array, rispettivamante di MAX_GIOCATTOLI elementi del tipo Giocattolo e MAX_BAMBINI elementi del tipo Bambino. MAX_GIOCATTOLI e MAX_BAMBINI, 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 esercizio 5.1 #define MAX_GIOCATTOLI 20 #define MAX_BAMBINI 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[MAX_GIOCATTOLI]; Bambino Bambini[MAX_BAMBINI]; Copyright The McGraw-Hill Companies srl 13

14 o 5.2 Si definiscano 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 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 esercizio 5.2 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 codicefiscale[16]; TipoDatiProprietario; typedef struct TipoDatiMotoveicolo motoveicolo; TipoDatiProprietario proprietario; TipoVocePRA; typedef struct TipoVocePRA elementi[10000]; int nelementi; TipoPRA; Copyright The McGraw-Hill Companies srl 14

15 o 5.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 esercizo 5.3 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. o 5.4 Definire un tipo di dati che contiene le informazioni riguardanti un esame sostenuto da un qualunque studente universitario. Queste informazioni si compongono di: nome dell'esame (nessun nome è più lungo di 40 caratteri), codice alfanumerico (al massimo 6 lettere, per esempio AG0012), voto compreso tra 18 e 30 e lode (si scelga un'opportuna rappresentazione dell'eventuale lode, tenendo conto che non sono ammesse le frazioni di voto), numero di crediti associati all'esame (sono ammesse le frazioni di credito, per esempio 7,5). Di ogni studente si vogliono memorizzare le seguenti informazioni: nome (si suppone che un nome non sia composto da più di 50 lettere), matricola (un numero naturale), data di immatricolazione, numero di esami sostenuti, lista degli esami sostenuti ( essendo un singolo esame descritto come in precedenza(, media dei voti riportati. Ogni studente può sostenere al massimo 29 esami. (se si ritiene utile, si possono definire altri tipi di dati, di supporto a quelli richiesti) Soluzione esercizio 5.4 typedef enum false, true boolean; typedef struct unsigned short giorno; unsigned short mese; unsigned int anno; Tipo_data; typedef struct char nome[40]; char codice[6]; unsigned short voto; boolean lode; Copyright The McGraw-Hill Companies srl 15

16 float crediti; Tipo_esame; typedef struct char nome[50]; unsigned int matricola; Tipo_data data_immatricolazione; Tipo_esame esami[29]; unsigned short numero_esami; float media; Tipo_studente; o 5.5 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 dell esercizio 5.5 #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, flauto, 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]; Copyright The McGraw-Hill Companies srl 16

17 o 5.6 Si vuole scrivere un programma che tiene traccia del numero di CD acquistati per ogni mese dell anno nell arco di 10 anni. Definire un tipo TipoDatiCD che contiene i dati di ogni CD: il titolo (lungo al massimo 50 caratteri), l autore (massimo 60 caratteri), l anno di pubblicazione, il numero di copie acquistate (anche più di una, se per esempio si vuole fare un regalo). Definire un tipo di dato CDAcqInMese che contiene l elenco dei CD comprati in un dato mese (si può assumere che non vengano acquistati più di 50 diversi titoli al mese) ed il numero di titoli diversi acquistati in quel mese. Definire una variabile globale elencocd che contiene l elenco dei CD acquistati mese per mese negli anni dal 1990 al Se si fa uso di tipi accessori, anche questi vanno definiti. Soluzione dell esercizio 5.6 #include <stdio.h> #define MAX_L_TITOLO 50 #define MAX_L_AUTORE 60 #define MAX_TIT_IN_MESE 50 typedef struct char titolo[max_l_titolo]; char autore[max_l_autore]; int anno; int n_copie; TipoDatiCD; typedef struct TipoDatiCD cd[max_tit_in_mese]; int n_titoli; CDAcqInMese; /* Le righe di 'elencocd' corrispondono agli anni dal 1990 al 1999, le colonne ai 12 mesi di ciascun anno */ CDAcqInMese elencocd[10][12]; Copyright The McGraw-Hill Companies srl 17

18 Capitolo 6 Strutture di controllo Strutture di controllo: for, switch, do-while. o 6.1 Con riferimento all esercizio 5.1, 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 new line ( \n ) premendo il tasto <ENTER> 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 <ENTER>): Immetti il nome di un bambino: Giovanni I giocattoli posseduti da Giovanni sono: Trenino elettrico Cavallo a dondolo Soluzione dell esercizio 6.1 #include <stdio.h> #define. typedef. main () Giocattolo Bambino string boolean int Giocattoli[MaxGiocattoli]; Bambini[MaxBambini]; NomeBambino; trovato; i, k, j, LunghNomeBamb, NumBambini; /* NumBambini indica il numero di bambini presenti nell elenco*/ /* parte di programma omessa. Comprenderà, tra l altro, il dialogo utente-macchina per acquisire i dati e memorizzarli nelle variabili Giocattoli e Bambini*/ printf ( Immetti il nome del bambino di cui vuoi conoscere i giocattoli:\n ); Copyright The McGraw-Hill Companies srl 18

19 i = 0; scanf("%c", &NomeBambino[i]); while (NomeBambino[i]!= '\n') i++; scanf("%c", &NomeBambino[i]) LunghNomeBamb = i; /* Si cerca nell array Bambini il nome immesso */ trovato = false; k = 0; while (! trovato && k < NumBambini) /* Si incrementa i fintanto che la condizione rimane vera */ for (i = 0; i <= LunghNomeBamb && Bambini[k].nome[i] == NomeBambino[i]; i++) ; if (i > LunghNomeBamb) trovato = true; k++; if (! trovato) printf ( Il bambino indicato non compare nell elenco\n ); 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*/ o 6.3 Realizzare un programma che legge da tastiera (e memorizza) una sequenza di caratteri alfabetici minuscoli (letti ad uno ad uno), terminata dal carattere # ; se l utente inserisce un carattere non valido (un carattere maiuscolo, un numero o un simbolo che non sia # ), il carattere non va memorizzato; la sequenza può essere lunga al massimo 100 elementi (escluso lo # ). Il programma riproduce sullo standard output la sequenza di caratteri, convertendone il contenuto secondo la cifratura ROT- 13. Questa cifratura prevede che ad ogni carattere che rappresenta la lettera tra le 26 dell'alfabeto in posizione i si sostituisca la lettera in posizione i+13 (mod 26). Ad esempio, digitando la sequenza di caratteri testodiprova#, il programma deve stampare la stringa di caratteri grfgbqvcebin. Si noti come applicando un numero pari di volte la trasformazione si riottenga il testo di partenza. Soluzione dell esercizio 6.3 #include <stdio.h> #define MAX_NUM 100 main() Copyright The McGraw-Hill Companies srl 19

20 char dati[max_num], dato_corr, trasf; int i, j, n_dati; n_dati = 0; do printf("inserisci carattere alfabetico (# per terminare, massimo %d caratteri): ", MAX_NUM); scanf(" %c", &dato_corr); if (dato_corr <= 'z' && dato_corr >= 'a') dati[n_dati] = dato_corr; n_dati++; while (dato_corr!= '#' && n_dati < MAX_NUM); printf("\nsequenza trasformata:\n"); for(i = 0 ; i < n_dati ; i++) trasf = 'a' + (dati[i]+13 - 'a') % 26; printf("%c", trasf); printf("\n"); o 6.4 Scrivere un programma che legge da tastiera delle righe di caratteri (di lunghezza massima 100 caratteri). Per ogni riga letta, il programma conta il numero di occorrenze di ogni vocale (sia che appaia in caratteri maiuscoli che in quelli minuscoli). Quando l'utente immette una riga vuota, il programma stampa un istogramma verticale (dall'alto al basso) con tanti asterischi quante sono le occorrenze di ogni vocale. Per esempio, se l'utente immette le seguenti righe: NEL mezzo DEL cammin DI nostra vita <ENTER> mi RITROVAI per una SELVA oscura <ENTER> CHE la diritta via era SMARRITA <ENTER> <ENTER> Il programma stampa il seguente istogramma: AEIOU ***** ***** **** **** *** *** *** * * * * * * * * * Copyright The McGraw-Hill Companies srl 20

21 Soluzione dell esercizio 6.4 #include <stdio.h> #define MAX_RIGA 100 main() char riga[max_riga+1]; unsigned int counter[5]; int i; /* Inizializza i contatori */ for( i = 0 ; i < 5 ; i++ ) counter[i] = 0; /* Leggi la prima riga */ printf("inserisci riga di caratteri (max %d elementi):\n", MAX_RIGA); i = 0; scanf("%c", &riga[i]); while (riga[i]!= '\n') i++; scanf("%c", &riga[i]); /* Analizza la riga */ while(riga[0]!= '\n') for( i=0 ; riga[i]!= '\n' ; i++ ) if (riga[i] == 'a' riga[i] == 'A') counter[0] += 1; if (riga[i] == 'e' riga[i] == 'E') counter[1] += 1; if (riga[i] == 'i' riga[i] == 'I') counter[2] += 1; if (riga[i] == 'o' riga[i] == 'O') counter[3] += 1; if (riga[i] == 'u' riga[i] == 'U') counter[4] += 1; /* Leggi la riga successiva */ printf("inserisci riga di caratteri (max %d elementi):\n", MAX_RIGA); i = 0; scanf("%c", &riga[i]); while (riga[i]!= '\n') i++; scanf("%c", &riga[i]); printf("\naeiou\n"); while ( counter[0] > 0 counter[1] > 0 counter[2] > 0 counter[3] > 0 counter[4] > 0 ) if (counter[0] > 0) printf("*"); counter[0] -= 1; Copyright The McGraw-Hill Companies srl 21

22 printf(" "); if (counter[1] > 0) printf("*"); counter[1] -= 1; printf(" "); if (counter[2] > 0) printf("*"); counter[2] -= 1; printf(" "); if (counter[3] > 0) printf("*"); counter[3] -= 1; printf(" "); if (counter[4] > 0) printf("*"); counter[4] -= 1; printf(" "); printf("\n"); o 6.5 Facendo riferimento all o 5.5, si scriva 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 dell esercizio 6.5 main() Copyright The McGraw-Hill Companies srl 22

23 TipoQuartetto ElencoQuartetti[MAX_QUARTETTI]; int numquartetti[max_quartetti]; /* inizializzazione delle variabili sopra dichiarate, 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 == pianoforte) trovato = 1; j++; if (trovato == 0) QuartettiSenzaPiano[numQuarSenzaPiano] = ElencoQuartetti[i]; numquarsenzapiano++; QuartettiConPiano[numQuarConPiano] = ElencoQuartetti[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); o 6.6 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. Copyright The McGraw-Hill Companies srl 23

24 Soluzione dell esercizio 6.6 #define MAX_PACC 64 typedef int TipoPacchetto[4]; typedef enum false, true boolean; main() TipoPacchetto corretti[max_pacc], non_corretti[max_pacc], corrente; int i, j, k, h, somma, n_corretti, n_non_corretti; boolean 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; somma += corrente[h]; if (ammissibile) if (somma % 2 == 0) for(h = 0 ; h < 4 ; h++) corretti[j][h] = corrente[h]; j++; for(h = 0 ; h < 4 ; h++) non_corretti[k][h] = corrente[h]; k++; i++; printf( Pacchetto immesso non ammissibile, reimmetterlo!\n ); n_corretti = j; n_non_corretti = k; Copyright The McGraw-Hill Companies srl 24

25 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 ); o 6.7 Con riferimento all o 5.6, si scriva un main() che, senza curarsi di come viene inizializzata la variabile elencocd (che supponiamo già caricata con i dati di interesse), esegue le seguenti operazioni: - chiede all utente di inserire un anno (compreso tra 1990 e 1999); - per ogni trimestre di quell anno (Gen-Mar, Apr-Giu, Lug-Set, Ott-Dic) stampa a video il numero di CD (contando anche le copie multiple dello stesso titolo) acquistati in quel periodo. Soluzione dell esercizio 6.7 #include <stdio.h> #define MAX_L_TITOLO 50 #define MAX_L_AUTORE 60 #define MAX_TIT_IN_MESE 50 typedef struct char titolo[max_l_titolo]; char autore[max_l_autore]; int anno; int n_copie; TipoDatiCD; typedef struct TipoDatiCD cd[max_tit_in_mese]; int n_titoli; CDAcqInMese; /* Le righe di 'elencocd' corrispondono agli anni dal 1990 al 1999, le colonne ai 12 mesi di ciascun anno */ CDAcqInMese elencocd[10][12]; main() Copyright The McGraw-Hill Companies srl 25

26 int anno; int i, j, k, totale_cd_trimestre; do printf("inserisci un anno compreso tra 1990 e 1999: "); scanf("%d", &anno); while (anno < 1990 anno > 1999); /* tramuto l'anno interno nella riga corrspondente dell'array doppio 'elencocd' */ anno = anno ; /* il ciclo piu' esterno conta i trimestri, quello piu' interno i mesi del trimestre */ for(i = 0 ; I < 4 ; i++) totale_cd_trimestre = 0; for(j = 0 ; j < 3 ; j++) /* conto il numero di CD acquistati nel mese i*3+j e li aggiungo al totale del trimestre*/ for(k = 0 ; k < elencocd[anno][i*3+j].n_titoli ; k++) totale_cd_trimestre += elencocd[anno][i*3+j].cd[k].n_copie; printf("totale CD del trimestre n. %d dell'anno %d: %d\n", i+1, anno+1990, totale_cd_trimestre); Copyright The McGraw-Hill Companies srl 26

27 Funzioni e procedure, parametri e valore di ritorno; stringhe. Capitolo 7 Funzioni e procedure o 7.1 Si consideri la seguente dichiarazione di tipo: typedef struct int campo1; int campo2; int campo3; int campo4; Struttura; Parte 1. Si scriva una funzione che riceva un parametro di tipo Struttura e produca come risultato un valore dello stesso tipo in cui i campi risultino invertiti (il valore di campo4 si trovi in campo1 e quello di campo3 in campo2). Parte 2. Si trasformi poi la funzione in una procedura che esegua la medesima operazione sulla variabile di tipo struttura che le è passata come parametro. Soluzione dell esercizio 7.1 Parte 1. Struttura InvertiStruttura (Struttura param) Struttura varloc; varloc.campo1 = param.campo4; varloc.campo2 = param.campo3; varloc.campo3 = param.campo2; varloc.campo4 = param.campo1; return varloc; Parte 2. void ProcInvertiStruttura (Struttura *param) Struttura varloc; varloc.campo1 = param->campo4; varloc.campo2 = param->campo3; varloc.campo3 = param->campo2; varloc.campo4 = param->campo1; param->campo4 = varloc.campo4; param->campo3 = varloc.campo3; param->campo2 = varloc.campo2; param->campo1 = varloc.campo1; Copyright The McGraw-Hill Companies srl 27

28 o 7.2 Data la seguente funzione calcola(): int calcola( int *a, int b, int *c) *a = *a * 2; b = b * 2; *c = *c - 2; return *a + b + *c; Qual è l'output del seguente frammento di programma? main() int z; int *y; int x; y = &x; z = 1; *y = 2; x = 3; z = calcola(&z,x,y); printf("%d, %d",z,x); Soluzione dell esercizio 7.2 Il frammento di programma stampa, nell ordine, i valori 9, 1. Infatti, prima della chiamata di calcola(), z ha valore 1, y contiene l indirizzo di x, ed x vale 3 (quindi *y vale anch esso 3). Durante l esecuzione di calcola(), x passa da 3 ad 1 con l istruzione *c = *c 2. z viene anch esso modificato (è passato per indirizzo), ma questa modifica viene poi annullata quando, al termine di calcola(), il valore ritornato dalla funzione è assegnato a z. Poichè calcola() ritorna 9 (la funzione era stata chiamata con z=1, x=3, *y=3), z alla fine vale 9. o 7.3 Data la seguente funzione calcola(): int calcola(int *a, int b, int *c) *a = *a * b; b = b * 2; *c = *a - b; return *a + b + *c; Ed il seguente programma: main() int z; int *y; Copyright The McGraw-Hill Companies srl 28

29 int x; y = &x; z = -1; *y = 1; x = 2; z = calcola(y,x,&z); Mostrare lo stato delle variabili x, y e z al termine dell'invocazione di calcola(). Soluzione dell esercizio 7.3 Alla fine del main(), la variabile x contiene il valore 4, la y punta a x, quindi *y a sua volta vale 4, mentre z vale 8. Infatti, prima della chiamata di calcola, z ha valore -1, y contiene l indirizzo di x, ed x vale 2 (quindi *y vale anch esso 3). Durante l esecuzione di calcola(), x passa da 2 ad 4 con l istruzione *a = *a * b. z viene anch esso modificato (è passato per indirizzo), ma questa modifica viene poi annullata quando, al termine di calcola(), il valore ritornato dalla funzione è assegnato a z. Poichè calcola ritorna 8 (la funzione era stata chiamata con z=-1, x=2, *y=2), z alla fine vale 8. o 7.4 Si scriva un insieme di dichiarazioni di tipo per la rappresentazione dello stato di un apparecchio TV. Esso deve contenere una descrizione dello stato del televisore (acceso/spento, canale, posizione dei vari controlli: luminosità, volume, ecc.). Si scrivano poi i prototipi (non le definizioni complete) di alcune funzioni corrispondenti ai comandi. Ad esempio, una funzione cambiacanale() potrebbe assegnare al televisore il canale impostato dall utente; un ulteriore funzione incrementacanale(), innescata dalla pressione del tasto corrispondente, dovrebbe determinare il passaggio dal canale attuale al successivo; ecc. Successivamente si scrivano le definizioni di almeno due delle procedure suddette. Soluzione dell esercizio 7.4 #include <stdio.h> #define INCREMENTO 0.1 typedef enum false, true boolean; typedef struct boolean acceso; unsigned int canale; float luminosita; /* Tra 0 e 1 */ float saturazione; /* Tra 0 e 1 */ float volume; /* Tra 0 e 1 */ Televisore; /* Prototipi delle funzioni */ void cambiacanale (Televisore *stato, unsigned int nuovocanale); void accendispegni (Televisore *stato); void incrementacanale (Televisore *stato); void decrementacanale (Televisore *stato); void incrementavolume (Televisore *stato); Copyright The McGraw-Hill Companies srl 29

30 void decrementavolume (Televisore *stato); void incrementasaturazione (Televisore *stato); void decrementasaturazione (Televisore *stato); void incrementaluminosita (Televisore *stato); void decrementaluminosita (Televisore *stato); main() Televisore stato; stato.acceso = false; printf ("%d", stato.acceso); accendispegni (&stato); printf ("%d", stato.acceso); accendispegni (&stato); printf ("%d", stato.acceso); void incrementacanale (Televisore *stato) if (stato->canale < 99) stato->canale = stato->canale + 1; void incrementavolume (Televisore *stato) if (stato->volume + INCREMENTO <= 1) stato->volume = stato->volume + INCREMENTO; void accendispegni (Televisore *stato) stato->acceso =! stato->acceso; definizione delle funzioni rimanenti. /* Per esempio, accendiamo e spegniamo il televisore */ /* Definizione delle funzioni */ o 7.5 Si scriva un programma che: Legge dallo standard input una sequenza di coppie di numeri reali <x i,y i > (per comodità si può assumere che tale sequenza non contenga più di KMAX elementi, essendo KMAX un valore costante noto a priori). Per ogni coppia <x i,y i > calcola il valore z i della funzione f(x i,y i ) = 20 x 2 i - y 3 i. Stampare sullo standard output la sequenza di valori z i in ordine crescente. Ad esempio, in corrispondenza della sequenza di input <0,5>, <2,4>, <1,3> il programma deve stampare in uscita la sequenza -125, -7, 16 E particolarmente apprezzata la costruzione di un programma che sia facilmente modificabile in modo da risolvere lo stesso problema facendo però riferimento ad una diversa funzione f(x i,y i ) (ad esempio f(x i,y i ) = sin(x i ). cos 3 (y i )) Se lo si ritiene opportuno è possibile fare uso di adeguati sottoprogrammi di servizio (ad esempio una procedura di ordinamento) senza codificarli ma limitandosi a dichiararne opportunamente l interfaccia. Soluzione dell esercizio 7.5 #include <stdio.h> Copyright The McGraw-Hill Companies srl 30

31 #define KMAX 10 typedef struct float x; float y; Coppia; float eleva (float base, unsigned int esp); float funzione (Coppia c); void ordina (float ris[], unsigned int n); /* Dichiarazione prototipi */ main() float risultati[kmax]; unsigned int ncoppie, i; Coppia c; printf ("Quante coppie (max %u): ", KMAX); scanf ("%u", &ncoppie); for (i = 0; i < ncoppie; i++) printf ("x: "); scanf ("%f", &c.x); printf ("y: "); scanf ("%f", &c.y); risultati[i] = funzione(c); ordina (risultati, ncoppie); for (i = 0; i < ncoppie; i++) printf ("risultato: %f\n", risultati[i]); /* Definizione delle funzioni */ /* Si assume che la funzione ordina() sia fornita dall'esterno */ float eleva (float base, unsigned int esp) unsigned int i; float tot = 1.0; for (i = 0; i < esp; i++) tot = tot * base; return tot; float funzione (Coppia c) return 20 * eleva (c.x, 2) - eleva (c.y, 3); Copyright The McGraw-Hill Companies srl 31

32 Capitolo 8 Introduzione alla programmazione ricorsiva La ricorsione. o 8.1 E data la funzione ricorsiva: int elabora(int n1, int n2) int a, b; a = n1 + 1; b = n2-1; if (b <= 0) return a; return alabora(a,b); Descrivere l'esecuzione della funzione in seguito all'invocazione: elabora(4,3). Soluzione dell esercizio 8.1 La sequenza di chiamate ricorsive della funzione elabora() è la seguente: elabora(4,3) elabora(5,2) elabora(6,1) Alla fine il valore ritornato dalla chiamata elabora(4,3) è 7. o 8.2 Sia data la funzione ricorsiva seguente: void elabora(unsigned int i) if (i <= 1) printf("%u\n", i); printf("%u", i % 2); elabora(i / 2); Descrivere l'esecuzione della funzione in seguito all'invocazione elabora(10), specificando che cosa rimane visualizzato sullo schermo alla fine dell esecuzione. Soluzione dell esercizio 8.2 La sequenza di chiamate ricorsive della funzione elabora() è la seguente: elabora(10) Copyright The McGraw-Hill Companies srl 32

33 elabora(5) elabora(2) elabora(1) Alla fine rimane visualizzata la seguente sequenza di valori: La funzione Elabora infatti calcola in maniera ricorsiva la rappresentazione binaria (scritta con la cifra meno significativa a sinistra) del numero passatole. o 8.3 Si consideri la seguente funzione, che calcola l n-simo numero di Fibonacci in modo ricorsivo: int fibonacci(unsigned int n) if (n == 0) return 0; if (n == 1) return 1; return fib(n 1) + fib(n 2); Si descriva la computazione di fib(4). Soluzione dell esercizio 8.3 La sequenza di chiamate ricorsive della funzione fibonacci() è la seguente: fibonacci(4) fibonacci(3) + fibonacci(2) fibonacci(2) + fibonacci(1) + fibonacci(1) + fibonacci(0) fibonacci(1) + fibonacci(0) o 8.4 Data la seguente definizione ricorsiva: f(x) = 0; per x = 0 f(x) = 2 + f(f(x-2)-2); per x > 0 1. Si determini per quali valori interi di x è definita la funzione e quale è il suo valore. 2. Si scriva una funzione ricorsiva che realizzi la funzione stessa. Il prototipo della funzione sia il seguente: int f1 (int x); 3. Si scriva una funzione che realizzi la funzione e restituisca come parametro il numero di chiamate ricorsive effettuate. Il prototipo della funzione sia il seguente: int f2 (int x, int *numchiamate); Copyright The McGraw-Hill Companies srl 33

34 NB: si supponga che, al momento della prima chiamata, il valore di numchiamate sia correttamente inizializzato a 0. Soluzione dell esercizio 8.4 Parte 1. La funzione è definita solo per x = 0. Parte 2. int f1(int x) if (x == 0) return 0; if (x > 0) return 2 + f(f(x-2)-2); printf ("ERRORE x=%d\n", x); exit(); Parte 3. int f2(int x, int *numchiamate) *numchiamate = *numchiamate + 1; if (x == 0) return 0; if (x > 0) return 2 + f(f(x-2, numchiamate)-2, numchiamate); printf ("ERRORE x=%d, nunchiamate=%d\n", x, *numchiamate); exit(); Copyright The McGraw-Hill Companies srl 34

35 Capitolo 9 Gestione dei file I file di testo e i file binari. o 9.1 Con riferimento all esercizio 5.4 si scriva la funzione inseriscimedia() che legge un file binario contenente i dati di tutti gli studenti di un ateneo, privi della media dei voti riportati e lo aggiorna inserendo nell apposito campo la media suddetta. La funzione dovrà a sua volta far uso di un ulteriore, appropriata, funzione che calcoli la media. E sufficiente fornire la dichiarazione di tale funzione ausiliaria, senza fornirne la definizione completa. Soluzione dell esercizio 9.1 #include <stdio.h> typedef enum false, true boolean; typedef struct unsigned short giorno; unsigned short mese; unsigned int anno; Tipo_data; typedef struct char nome[40]; char codice[6]; unsigned short voto; boolean lode; float crediti; Tipo_esame; typedef struct char nome[50]; unsigned int matricola; Tipo_data data_immatricolazione; Tipo_esame esami[29]; unsigned short numero_esami; float media; Tipo_studente; /* La funzione media() non viene implementata */ float media (Tipo_esame esami[], unsigned short numesami); void inseriscimedia(char nomefile[]); main() /* Il main del programma... */ Copyright The McGraw-Hill Companies srl 35

36 void inseriscimedia(char nomefile[]) FILE *filedati; Tipo_studente studente; filedati = fopen(nomefile, "r+"); fread (&studente, sizeof(studente), 1, filedati); while (! feof(filedati)) studente.media = media (studente.esami, studente.numero_esami); fread (&studente, sizeof(studente), 1, filedati); /* Torna indietro per scrivere le modifche sullo stesso record */ fseek (filedati, -sizeof(studente), SEEK_CUR); fwrite (&studente, sizeof(studente), 1, filedati); fclose (filedati); o 9.2 Con riferimento all o 5.5 si definiscano i tipi TipoStrumentista e TipoQuartetto come nell'esercizio suddetto. 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. Si supponga poi 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 a scelta una delle seguenti varianti. 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. Copyright The McGraw-Hill Companies srl 36

37 Suggerimento per le varianti b e c: per troncare un file f alla lunghezza data dalla posizione corrente nel file, l istruzione da usare è: ftruncate(fileno(f), ftell(f)); Soluzione dell esercizio 9.2 Definizione dei tipi; parte comune a tutte le varianti. #define L_NOME_MUS 40 #define L_NOME_QUAR 30 #define MAX_QUARTETTI 100 #define L_NOME_CONC 40 typedef struct short giorno; short mese; int anno; TipoData; typedef enum pianoforte, basso, batteria, sax, trombone, flauto, 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; typedef struct char nome_loc[l_nome_conc + 1]; TipoData data; TipoQuartetto quartetto; TipoConcerto; 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)) Copyright The McGraw-Hill Companies srl 37

38 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; fclose(nf); return 0; if (fclose(nf) == 0) return 1; return 0; 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; fclose(tmp_f); return 0; rewind(f_concerti); Copyright The McGraw-Hill Companies srl 38

39 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; 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; return 0; 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 posizione in cui devo scrivere (pos_next_write) e la prossima posizione 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 riscrivo nel file f_concerti alla prossima posizione in cui devo scrivere. Prima di scrivere, però, devo memorizzare la posizione corrente nel file perchè a questa posizione 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; Copyright The McGraw-Hill Companies srl 39