Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
III edizione
Esercizi su Web
Informatica: Arte e Mestiere, Terza edizione
Stefano Ceri, Dino Mandrioli, Licia Sbattella Esercizi
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
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.
Capitolo 3
Codifica degli algoritmi in un linguaggio di alto livello
Esercizio 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.
Esercizio 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
…
Capitolo 4
Esecuzione di programmi C su macchine reali
Esercizio 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: 13.863
Inserisci numero: -15.625
Inserisci numero: 4
Inserisci numero: 38.192
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)
Esercizio 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
***
******
***
*****
#include <stdio.h>
main()
{
n_dati = 0;
do
{
printf("Inserisci numero (0 per terminare): ");
scanf("%u", &dato_corr);
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;
}
Esercizio 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.
#include <stdio.h>
main()
{
float x, s, min, max;
unsigned int n, num_dati;
s = 0.0;
for (n = 0; n < num_dati; n++)
{
printf ("Numero: ");
Capitolo 5
Tipi di dato
Esercizio 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.
typedef struct
{
int giorno;
int mese;
int anno;
} Data;
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];
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;
Esercizio 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)
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;
typedef struct
{
char nome[50];
unsigned int matricola;
Tipo_data data_immatricolazione;
Tipo_esame esami[29];
unsigned short numero_esami;
float media;
} Tipo_studente;
Esercizio 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)
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];
#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;
Capitolo 6
Strutture di controllo
Esercizio 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
#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 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”);
Esercizio 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.
main()
n_dati = 0;
do {
printf("Inserisci carattere alfabetico (# per terminare, massimo %d caratteri): ",
MAX_NUM);
scanf(" %c", &dato_corr);
printf("\nSequenza trasformata:\n");
for(i = 0 ; i < n_dati ; i++)
{
trasf = 'a' + (dati[i]+13 - 'a') % 26;
printf("%c", trasf);
}
printf("\n");
}
Esercizio 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>
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;
else if (riga[i] == 'e' || riga[i] == 'E')
counter[1] += 1;
else if (riga[i] == 'i' || riga[i] == 'I')
counter[2] += 1;
else if (riga[i] == 'o' || riga[i] == 'O')
counter[3] += 1;
else 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;
}
else
{
Esercizio 6.5
Facendo riferimento all’Esercizio 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.
Esercizio 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.
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;
}
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, reimmetterlo!\n”);
}
}
n_corretti = j;
n_non_corretti = k;
Esercizio 6.7
Con riferimento all’Esercizio 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.
#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;
main()
do
{
printf("Inserisci un anno compreso tra 1990 e 1999: ");
scanf("%d", &anno);
} while (anno < 1990 || anno > 1999);
Capitolo 7
Funzioni e procedure
Esercizio 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 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;
}
return *a + b + *c;
}
y = &x;
z = 1;
*y = 2;
x = 3;
z = calcola(&z,x,y);
printf("%d, %d",z,x);
}
Esercizio 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;
y = &x;
z = -1;
*y = 1;
x = 2;
z = calcola(y,x,&z);
}
Esercizio 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.
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;
main()
{
Televisore stato;
/* Per esempio, accendiamo e spegniamo il televisore */
stato.acceso = false;
printf ("%d", stato.acceso);
accendiSpegni (&stato);
printf ("%d", stato.acceso);
accendiSpegni (&stato);
printf ("%d", stato.acceso);
}
/* Definizione delle funzioni */
void incrementaCanale (Televisore *stato)
{
if (stato->canale < 99)
stato->canale = stato->canale + 1;
}
Esercizio 7.5
Si scriva un programma che:
• Legge dallo standard input una sequenza di coppie di numeri reali {<xi,yi>} (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 <xi,yi> calcola il valore zi della funzione f(xi,yi) = 20⋅x2i - y3i.
• Stampare sullo standard output la sequenza di valori { zi } 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(xi,yi) (ad esempio f(xi,yi) = sin(xi). cos3(yi))
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.
typedef struct
{
float x;
float y;
} Coppia;
/* Dichiarazione prototipi */
float eleva (float base, unsigned int esp);
float funzione (Coppia c);
void ordina (float ris[], unsigned int n);
main()
{
float risultati[KMAX];
unsigned int nCoppie, i;
Coppia c;
Capitolo 8
Introduzione alla programmazione ricorsiva
La ricorsione.
Esercizio 8.1
E’ data la funzione ricorsiva:
a = n1 + 1;
b = n2 - 1;
if (b <= 0)
return a;
else
return alabora(a,b);
}
Esercizio 8.2
Sia data la funzione ricorsiva seguente:
Esercizio 8.3
Si consideri la seguente funzione, che calcola l’n-simo numero di Fibonacci in modo
ricorsivo:
fibonacci(4)
fibonacci(3) + fibonacci(2)
fibonacci(2) + fibonacci(1) + fibonacci(1) + fibonacci(0)
fibonacci(1) + fibonacci(0) + 1 + 1 + 0
1+0+1+1+0
3
Esercizio 8.4
Data la seguente definizione ricorsiva:
f(x) = 0; per x = 0
f(x) = 2 + f(f(x-2)-2); per x > 0
Parte 1.
La funzione è definita solo per x = 0.
Parte 2.
int f1(int x)
{
if (x == 0)
return 0;
else if (x > 0)
return 2 + f(f(x-2)-2);
else
{
printf ("ERRORE x=%d\n", x);
exit();
}
}
Parte 3.
int f2(int x, int *numChiamate)
{
*numChiamate = *numChiamate + 1;
if (x == 0)
return 0;
else if (x > 0)
return 2 + f(f(x-2, numChiamate)-2, numChiamate);
else
{
printf ("ERRORE x=%d, nunChiamate=%d\n", x, *numChiamate);
exit();
}
}
Capitolo 9
Gestione dei file
Esercizio 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.
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;
main()
{
/* Il main del programma... */
}
Esercizio 9.2
Con riferimento all’Esercizio 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.
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;
Variante b.
#include <string.h>
#include <stdio.h>
FILE *f_concerti;
Variante c.
#include <string.h>
#include <stdio.h>
FILE *f_concerti;
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;
/* mi riporto sulla posizione che è la prossima in cui scrivere (cioè appena dopo l’ultimo
concerto che va conservato, e tronco il file (il file riscritto è in generale più corto
del file originario */
if (fseek(f_concerti, pos_next_write, SEEK_SET))
return 0;
ftruncate(fileno(f_concerti), ftell(f_concerti));
return 1;
}
Esercizio 9.3
Si abbia un file (binario) “FileFatture.dat” 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.
pos_corr = ftell(file_fatture);
fseek(file_fatture, sizeof(TipoFatture)*(num_ord-1), SEEK_SET);
fread(&fattura, sizeof(TipoFattura), 1, file_fatture);
Esercizio 9.4
Si scriva un programma che legge un file di parole appartenenti al vocabolario
italiano o al vocabolario inglese e le ristampa su due file separati, uno per le parole
inglesi e uno per quelle italiane.
Si assuma che il programma abbia accesso (in memoria o su file) ai vocabolari delle
due lingue.
NB1. Si ponga attenzione a casi particolari come il fatto che una parola del file
originario non si trovi in nessuno dei due vocabolari o che si trovi in entrambi, ecc.
NB2. Non è necessaria una codifica in tutti i dettagli del programma completo: si può
invece far uso di opportune procedure limitandosi a definire con precisione il loro uso
(la loro interfaccia) senza per questo codificarne l'implementazione.
main()
{
char nomeFileParole[100], nomeFileParoleIta[100], nomeFileParoleIng[100];
Capitolo 10
Strutture dati dinamiche
Le liste.
Esercizio 10.1
Parte a.
Si considerino la tradizionale dichiarazione:
struct EL
{
int Info;
struct EL *Prox;
};
typedef struct EL ElemLista;
typedef ElemLista *ListaDiInteri;
E la seguente funzione:
int sconosciuta (ListaDiInteri Lista)
{
int x = 0;
ListaDiInteri cursore;
cursore = Lista;
while (cursore != NULL)
{
if (cursore->Info > x)
x = cursore->Info;
cursore = cursore->Prox;
}
return x;
}
L1
6 4 18 -4 4
L2
In generale, qual’è il risultato prodotto dalla funzione per una generica lista di interi?
Parte b.
Si ricodifichi la funzione sconosciuta in forma ricorsiva.
Parte a.
La funzione produce come risultato 18, quando applicata alla lista L1 e 0 quando
applicata a L2. In generale essa il valore massimo contenuto nella lista se essa
contiene almeno un numero positivo; 0 in caso contrario (lista vuota o contenente solo
numeri non positivi).
Parte b.
Si definisca in primo luogo la funzione ausiliaria max():
Int max(int p1, p2)
{
if (p1 > p2)
return p1;
else
return p2;
}
Esercizio 10.2
Si vuole realizzare una serie di funzioni per la manipolazione di un archivio di numeri
di carte di credito. Ogni carta di credito è definita dai seguenti dati: nome del titolare
della carta (massimo 80 caratteri), numero della carta di credito (un codice di
esattamente 16 cifre), massimale di spesa mensile per la carta (in euro).
Il programma deve essere sviluppato nei seguenti punti:
Punto a.
Definire il tipo TipoCartaDiCredito, che contiene i dati di una singola carta. Definire
quindi i tipi TipoElemListaCarte e TipoListaCarte (con l’ovvio significato degli
identificatori), necessari per definire una lista di carte di credito realizzata mediante
puntatori.
Dichiarare i prototipi di tutte e sole le seguenti funzioni (senza darne ancora
l’implementazione!):
• carica_lista_da_file(): riceve in ingresso il nome del file binario in cui è
memorizzato l’archivio e ritorna una lista contente i dati dell’archivio; si assuma
che il file binario contenga una sequenza di dati TipoCartaDiCredito; la lista
ritornata dalla funzione non è ordinata secondo nessun criterio particolare; se non
è possibile recuperare i dati da file, la funzione ritorna una lista vuota;
• scarica_lista_su_file(): riceve in ingresso una lista di carte di credito ed un nome di
file, e scrive la lista nel dato file; se il file già esiste, viene cancellato, altrimenti
viene creato ex-novo; la funzione ritorna 1 se la scrittura avviene con successo, 0
altrimenti;
• stampa_ListaCarte(): riceve in ingresso una lista di carte di credito, e la stampa a
video;
• estrai_carte_da_lista(): riceve in ingresso una lista di carte di credito, e ne ritorna
una nuova, che contiene solo le carte di credito la cui ultima cifra è ‘7’.
Punto b.
Implementare la funzione carica_lista_da_file() dichiarata al punto a). Eventuali librerie
standard del C usate per implementare la funzione vanno dichiarate prima del corpo
della funzione stessa, mediante l’apposita clausola include. Eventuali ulteriori
sottoprogrammi ausiliari dovranno essere definiti completamente.
Punto c
Implementare la funzione estrai_carte_da_lista dichiarata al Punto a. Eventuali librerie
standard del C usate per implementare la funzione vanno dichiarate prima del corpo
della funzione stessa, mediante l’apposita clausola #include. Eventuali ulteriori
sottoprogrammi ausiliari dovranno essere definiti completamente.
Si fornisca una duplice implementazione della funzione: una versione iterativa e una
ricorsiva.
typedef struct
{
char nome[MAX_L_NOME+1];
char numero[16];
float massimale;
} TipoCartaDiCredito;
struct EL
{
TipoCartaDiCredito info;
struct EL *next;
};
Punto b.
#include <stdlib.h>
f = fopen(nome_file, “rb+”);
if(f == NULL)
return NULL;
while (fread(&carta, sizeof(TipoCartaDiCredito), 1, f) == 1)
{
ptr_el = malloc(sizeof(TipoElemListaCarte));
ptr_el->info = carta;
ptr_el->next = l;
l = ptr_el;
}
return l;
}
Esercizio 10.3
Si vuole realizzare un programma GestisciNomi che realizza le seguenti funzionalità:
• legge dal file “ElencoNomi” un elenco di nomi (ogni nome si trova su una riga
separata);
• man mano che i nomi vengono letti, essi vengono inseriti in una lista, la quale
viene mantenuta ordinata secondo l’ordine alfabetico;
• quando non ci sono più nomi da leggere dal file, la lista viene stampata a video;
• viene creata una nuova lista, che contiene gli stessi elementi della precedente, ma
in ordine inverso rispetto all’originale (NB: la lista originale deve essere lasciata
inalterata);
• la lista inversa viene stampata.
Si precisa che il file da cui vengono letti i nomi è in formato carattere (non binario!),
con ogni nome su una riga diversa; si presume che non ci siano righe vuote. Ogni riga
è lunga al massimo 80 caratteri.
Il programma deve essere sviluppato nei seguenti punti:
Punto a.
Definire i tipi TipoElemListaNomi e TipoListaNomi (con l’ovvio significato degli
identificatori), quindi dichiarare i prototipi di tutte e sole le seguenti funzioni (senza
darne ancora l’implementazione!):
struct EL
{
char info[MAX_L_NOME + 1];
struct EL *next;
};
Punto b.
#include <stdio.h>
main()
{
TipoListaNomi lista_Att, inversa;
char nome[MAX_L_NOME + 1];
FILE *f;
f = fopen(“ElencoNomi”, “r”);
if(f == NULL)
init_ListaNomi(&inversa);
cursor = lista_Inv;
while(cursor != NULL)
{
temp = malloc(sizeof(TipoElemListaNomi));
strcpy(temp->info, cursor->info);
temp->next = inversa;
inversa = temp;
cursor = cursor->next;
}
return inversa;
}
if (lista == NULL)
{
temp = malloc(sizeof(TipoElemListaNomi));
temp->next = NULL;
strcpy(temp->info, (*lista)->info);
*lista = temp;
}
else
inserisci_in_coda(&((*lista)->next), nome);
}
if(lista == NULL)
return NULL;
else
{
inversa = inverti_ListaNomi(lista_Inv->next);
inserisci_in_coda(&inversa, lista_Inv->info);
return inversa;
}
}
Esercizio 10.4
Si considerino le seguenti dichiarazioni:
/* xxx denota una qualsiasi dichiarazione del tipo TipoElemento che qui non interessa
Sull’insieme dei valori di TipoElemento è definita una relazione d’ordine totale
implementata dalla funzione precede() dichiarata in seguito*/
typedef xxx TipoElemento;
L1 3 10 24
L2 5 56
L 3 5 10 24 56
lista_unione = NULL;
curr1 = lista1;
curr2 = lista2;
if (curr1 == NULL && curr2 == NULL)
return lista_unione;
else if (curr1 == NULL)
copia(lista2, &lista_unione);
else if (curr2 == NULL)
copia(lista1, &lista_unione);
else
{
lista_unione = malloc(sizeof(TipoElementoLista));
lista_unione->next = NULL;
curr = lista_unione;
if (precede(curr1->info, curr2->info))
{
lista_unione->info = curr1->info;
curr1 = curr1->next;
}
else
{
lista_unione ->info = curr2->info;
curr2 = curr2->next};
}
while (curr1 != NULL && curr2 != NULL)
{
temp = malloc(sizeof(TipoElementoLista));
temp->next = NULL;
curr->next = temp;
curr = temp;
if (precede(curr1->info, curr2->info))
*lista2 = NULL;
curr1 = lista1;
if (curr1 != NULL)
{
temp = malloc(sizeof(TipoElementoLista));
temp->next = NULL;
temp->info = curr1-> info;
*lista2 = temp; curr2 = temp; curr1 = curr1->next;
}
while (curr1 != NULL)
{
temp = malloc(sizeof(TipoElementoLista));
temp->next = NULL;
temp->info = curr1->info;
curr2->next = temp; curr2 = temp; curr1 = curr1->next;
}
}
Esercizio 10.5,
Punto a.
Si consideri il seguente programma:
#include <stdio.h>
#include <stdlib.h>
main ()
{
int **P; int *Q;
Q = malloc(sizeof(int));
P = &Q;
**P = 5;
*Q = 4;
printf(“il valore finale di **P è %d \n”, **P);
}
main ()
{
int **P; int *Q; int x;
x = 27;
Q = malloc(sizeof(int));
P = malloc(sizeof(Pint));
*P = Q;
**P = 4;
Q = &x;
P = &Q;
*Q = 5;
P Q
P Q
*Q = 4 però riscrive il valore 4 nella stessa cella dove era stato scritto 5. Perciò il
risultato finale è l’output seguente:
il valore finale di **P è 4
Punto b.
Dopo l’esecuzione delle istruzioni:
x = 27;
Q = malloc(sizeof(int));
P = malloc(sizeof( Pint));
*P = Q;
P Q
27
Capitolo 14
Archivi e basi di dati
Esercizio 14.1
Siano date le seguenti relazioni:
persona (cod_fiscale, nome, eta, sesso)
automobile (targa, proprietario, colore)
Esercizio 14.2
Partendo dalle relazioni definite nell’esercizio precedente e riempite con i dati
seguenti:
nome eta
Verdi 26
Grandi 50
SELECT targa
FROM persona, automobile
WHERE persona.cod_fiscale = automobile.proprietario And persona.eta < 30
targa
AG125FA
FA143XX
GA766AG
FF111GG
SELECT nome
FROM persona, automobile
WHERE persona.cod_fiscale = automobile.proprietario And persona.sesso='M'
And automobile.colore = 'rosso'
nome
Rossi
Bianchi
Capitolo 18
La visione dei sistemi informatici da parte dell’utente finale
Esercizi EXCEL.
Esercizio 18.1
Si costruisca un archivio, usando un qualsiasi foglio elettronico, contenente
informazioni relative a un insieme di dipendenti di un'azienda. Tali informazioni
devono contenere, tra l'altro:
- Nome e cognome della persona
- Data di nascita
- Data di assunzione
- Retribuzione annua lorda
Altre informazioni possono essere inserite a discrezione dello studente.
Un ulteriore campo, il cui valore deve essere opportunamente calcolato sulla base
delle informazioni precedenti, deve indicare l'ammontare annuo della pensione cui il
soggetto ha eventualmente diritto, sulla base della seguente definizione:
Se il soggetto ha maturato un'anzianità di servizio ≥ 35 anni (per semplicità si può
calcolare tale anzianità assumendo che l'anzianità di servizio sia il periodo di tempo
intercorso dalla data di assunzione alla data attuale) e < 45 anni la pensione cui ha
diritto è il 60% della retribuzione annua lorda. Se il soggetto ha maturato un'anzianità
di servizio ≥ 45 anni la pensione cui ha diritto è il 90% della retribuzione annua lorda
(la pensione non spetta a chi ha un'anzianità inferiore a 35 anni).
Esercizio 18.2
Si risolva l'esercizio 7.5 utilizzando un foglio elettronico. In tal caso i dati di ingresso
e di uscita devono trovarsi in apposite posizioni del foglio elettronico.
Registro IVA
Numero Dati del Imponibile Imposta al Imponibile Imposta al Totale
progressivo cliente al 10% 10% al 20% 20%
Esercizio 18.4
Il signor Rossi vuole tenere sotto controllo i propri conti mensilmente. All’uopo egli
desidera registrare ogni mese entrate e uscite, divise in opportune categorie, in modo
da sapere se e quanto ha guadagnato o perso; inoltre egli desidera tenere sotto
controllo anche il proprio conto corrente bancario, tenendo traccia di tutte le
operazioni di prelievo-deposito effettuate in modo da poter sapere in ogni momento di
quanto denaro egli dispone.
Si costruisca mediante Framework uno strumento che soddisfi le esigenze del signor
Rossi.
Parte Facoltativa
Si osservi che solitamente pagamenti (e incassi) possono avvenire sia in contanti che
mediante operazioni bancarie (bonifici, assegni, ...). Ad esempio: lo stipendio
potrebbe essere accreditato automaticamente dal datore di lavoro sul cc del signor
Rossi. Alcune spese egli potrebbe effettuarle pagando in assegni, ma altre usando
contante che egli preleva ogni tanto dalla banca. In questo modo le operazioni sul cc
non coincidono necessariamente con le operazioni di entrata-uscita.
Esercizio 18.5
Si vuole ripartire la spesa di costruzione di una strada a fondo chiuso sulla quale
insistono quattro condomini di nome Primula, Violetta, Fiordaliso e Gelsomino,
composti rispettivamente da 8, 6, 12 e 4 appartamenti e che distano dall’inizio della
strada rispettivamente 10, 100, 250 e 300 metri. La spesa, di complessive L
50.000.000, va ripartita in base al numero di appartamenti di ogni condominio, pesato
con la sua distanza dall’inizio della strada. Si costruisca un foglio elettronico che,
tramite l’introduzione dei dati del problema, permetta di ottenere una ripartizione
della spesa in modo analogo a quanto mostrato nella seguente tabella.
Primula 10 8 80 819672
Violetta 100 6 600 6147541
Fiordaliso 250 12 3000 30737705
Gelsomino 300 4 1200 12295082
Totali 4880 50000000