Questa è una vecchia versione del documento!
Indice
Esercitazione 1
Esercizi proposti su: puntatori, stringhe, puntatori a funzione, argc-argv, allocazione/deallocazione dinamica, ….
Cercare di completare tutti gli esercizi durante le ore di laboratorio e nel caso come homework.
In caso di difficoltà su aspetti del C, discuterne con il docente.
Esercizio 1 (sul debugging con gdb)
Usare il debugger gdb per trovare gli errori in questo programma C (ce ne sono almeno 2):
#include <stdlib.h> #include <stdio.h> #define N 32 int main() { int *A = malloc(N); int i=0; while(++i<N) { A[i] = !(i%2); } printf("stampa ...\n"); for(int i=0;i<N;++i) printf("%d ", A[i]); printf("\n"); printf("exiting ...\n"); free(A); return 0; }
Copiare il codice precedente in 'debugging.c'. Compilare il codice con l'opzione “-g”, ad esempio:
gcc -std=c99 -Wall -Werror -g debugging.c -o debugging
quindi lanciare l'eseguibile e verificare quali errori produce, poi usando il debugger:
$ gdb ./debugging
è possibile fermare il programma durante la sua esecuzione (ad esempio mettendo un breakpoint all'inizio del 'while' – (gdb) b 8 ), ispezionare le variabili (ad esempio '(gdb) p A[1]', '(gdb) next', etc..
Esercizio 2
Scrivere una funzione “swapPtr” che swappa due puntatori qualsiasi:
void swapPtr(void **ptr1, void **ptr2);
Testare la funzione con un semplice programma che stampi il vaore puntato prima e dopo lo swap.
Esercizio 3
Data una stringa con caratteri minuscoli e maiuscoli, trasformarla in una stringa con tutti i caratteri maiuscoli. NOTE: è possibile utilizzare le funzioni di libreria 'islower' e 'toupper' definite in ctype.h (vedere man 3 islower). Ad esempio usare la stringa:
char str[]="Questa e' una Stringa non tanto Lunga che CONtiene 123 !";
il risultato deve essere:
printf("%s\n", str); $ QUESTA E' UNA STRINGA NON TANTO LUNGA CHE CONTIENE 123 !
Una variante di questo esercizio è quello di trasformare i caratteri maiuscoli in minuscoli e viceversa.
Esercizio 4
Scrivere un programma che dato un vettore di un certo numero di elementi esegua la somma o il prodotto di tutti gli elementi del vettore a seconda del parametro passato come input al programma. Se l'argomento è '-s' dovrà essere effettuata la somma degli elementi, se l'argomento è '-m' dovrà essere effettuato il prodotto. Scrivere il programma in modo generico utilizzando un puntatore a funzione. Usare il seguente tipo puntatore a funzione:
// tipo puntatore a funzione, prima argomento il puntatore al vettore, secondo argomento la size del vettore typedef long (*function_t)(long *, size_t);
Esercizio 5
Scrivere una funzione 'mystrcat' con la seguente segnatura:
const char *mystrcat(char *prima, ...);
che prenda un numero di stringhe variabili e che concateni tutte le stringhe alla 'prima' con lo stesso comportamento della funzione di libreria 'strcat' (man 3 strcat). Utilizzare il seguente main:
int main() { char prima[1024] = "prima"; printf("%s\n", mystrcat(prima, "seconda", "terza lunga", "quarta", "quinta", "ultima!", NULL)); return 0; }
NOTA: A che cosa serve l'ultimo argomento 'NULL' ?
Esercizio 6
Scrivere un programma che dato un intero A a 32bit (usare i tipi int16_t, int32_t definiti in #include<stdint.h>) calcoli lo 'xor bit a bit' dei 2 byte piu' significativi (quelli di sinistra) con i 2 byte meno significativi. L'operatore di xor bit a bit è '^' (es. 0101 ^ 1100 = 1001). Dato A1 in formato network-byte-order di A (cioè formato big-endian, utilizzare htonl – vedi man 3 htonl), riordinare i byte di A1 in host-byte-order (cioè formato little-endian per sistemi Intel/AMD based). Vedere man 3 byteorder. Dato il numero in formato esadecimale 0x1234567:
high <------------------- low --------------------------------- | 0x01 | 0x23 | 0x45 | 0x67 | little-endian --------------------------------- --------------------------------- | 0x67 | 0x45 | 0x23 | 0x01 | big-endian ---------------------------------
Esercizio 7
Scrivere un programma che, dato un array di N elementi interi, costruisca un albero binario di ricerca (cioè per ogni nodo dell'albero, l'elemento del nodo è maggiore di tutti gli elementi del sottoalbero di sinistra e minore o uguale di tutti gli elementi del sottoalbero di destra). Implementare le seguenti funzioni:
struct node_t *buildTree(long elem, struct node_t *t); // costruisce l'albero e restituisce il nodo radice long getMin(struct node_t *root); // restituisce il valore minimo long getMax(struct node_t *root); // restituisce il valore massimo void printInOrder(struct node_t *root); // stampa gli elementi in modo ordinato void deleteTree(struct node_t *root); // cancella tutti i nodi dell'albero
Definire il tipo 'struct node_t' opportunamente. Implementare tutte le funzioni usando la ricorsione.
Esercizio 8
Scrivere un programma (il cui eseguibile chiameremo 'stringsort') che prende come argumenti da linea di comando un certo numero di stringhe (un numero arbitrario) e le ordina alfanumericamente utilizzando la chiamata di libreria 'qsort' (man 3 qsort).
Testare il programma come segue (vedere i manuali dei comandi 'cat' e 'xargs'):
1. preparare un file con il comando cat nel modo seguente:
$> cat << FINE > filediinput.txt > questa è la prima riga > Questa è la seconda più lunga > AAA questa è quella che deve finire per prima > jjj questa è prima della prima riga > zzz questa è ultima.... > FINE
2. lanciare il comando (il simbolo '|' mette in pipeline l'output del comando che precede con l'input del comando che segue) :
$> cat filediinput.txt | xargs -I {} echo \"{}\" | xargs ./stringsort
3. verificare che l'output sia correttamente ordinato.
Che cosa fa il comando 'xargs' come secondo elemento della pipeline ?
Esercizio 9
Non utilizzando la funzioni di libreria 'getopt' (man 3 getopt), scrivere un programma che effettua il parsing della linea di comando (argv) riconoscendo le seguenti opzioni:
-n <numero> -s <stringa> -m <altro-numero> -h.
Il programma dovrà stampare le opzioni riconosciute con il relativo argomento. L'opzione -h non ha argomento e corrisponde al messaggio di help (program usage). Se e' presente l'opzione -h dovra' essere stampato solo il messaggio di usage cioè:
nome-programma -n <numero> -s <stringa> -m <numero> -h
Se ci sono opzioni non riconosciute queste dovranno essere stampate a video con il messaggio “opzione X non riconosciuta”. Testare il programma con i seguenti casi (supponiamo che l'eseguibile si chiami cmdlineparsing):
cmdlineparsing -n 10 -m 11 -s 'ciao mondo' cmdlineparsing -n 10 -h // deve stampare solo il messaggio di usage cmdlineparsing -n 10 -k 12 // k e' una opzione non riconosciuta cmdlineparsing ----n 10 -s-s 'ciao mondo' // deve stampare -n: 10 e -s: -s cmdlineparsing -n10 -m11 -s'ciao mondo' // deve stampare gli argomenti come nel primo caso cmdlineparsing -n -m 11 // deve stampare un messaggio di errore per -n