====== 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. ===== Illustrazione uso gdb ===== Vediamo come si usa il debugger [[http://www.gnu.org/software/gdb| gdb]] usando [[usogdb|questo]] codice non funzionante. I comandi del gdb da ricordare sono: \\ - run ( r ), break ( b ), step ( s ), next ( n ), print ( p ), set args , backtrace ( bt ), finish, continue ( c ), quit ( q ). ===== Esercizio 1 ===== Usare il debugger gdb (o per chi vuole la sua interfaccia grafica [[http://www.gnu.org/software/ddd|ddd]] ) per trovare gli errori in questo programma C (ce ne sono almeno 2): #include #include #define N 32 int main() { int *A = malloc(N); int i=0; while(++i 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) 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 in C è '^' (es. 0101 ^ 1100 = 1001). Dato A1 contenente A in formato network-byte-order (cioè formato big-endian, utilizzare A1=htonl(A) -- vedi man 3 htonl), riordinare esplicitamente i byte di A1 in host-byte-order (cioè formato little-endian per sistemi Intel/AMD based) senza usare la funzione 'ntohl'. Dato il numero in formato esadecimale 0x1234567, i due formati hanno la seguente rappresentazione in memoria: high <------------------- low --------------------------------- | 0x01 | 0x23 | 0x45 | 0x67 | little-endian --------------------------------- --------------------------------- | 0x67 | 0x45 | 0x23 | 0x01 | big-endian --------------------------------- 3 2 1 0 ===== Esercizio 7 (binaryTree) ===== 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 -s -m -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 -s -m -h Se ci sono opzioni non riconosciute queste dovranno essere stampate a video con il messaggio "opzione X non riconosciuta". Per convertire le stringhe in interi usare la funzione di libreria //atoi// (vedere man 3 atoi) o meglio ancora la funzione //strtol// (vedere man strtol). 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 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