====== Esercitazione 2 ====== ===== Esercizio 1: ''getopt'' ===== Scrivere un programma C che prende in ingresso 4 opzioni da linea di comando codificate con '-n', '-m', '-o', '-h' nel modo seguente: nome-programma -n -m -o -h Il programma dovrà stampare sullo standard output le opzioni riconosciute valide con il relativo argomento. L'opzione '-h' non ha argomento e corrisponde al messaggio di uso. Effettuare il parsing della command line utilizzando la funzione di libreria ''getopt'' (man 3 getopt), ===== Esercizio 2: puntatori a funzioni ===== Estendere l'esercizio 1 associando ad ogni opzione del main una funzione che ne faccia la gestione e la stampa sullo standard output. Ad esempio, la gestione dell'opzione '-n' viene fatta dalla funzione 'arg_n(const char*)' la quale convertirà l'argomento associato all'opzione ad intero e farà la stampa sullo standard output, ritornado -1 in caso di errore e 0 in caso di successo. Memorizzare tali funzioni in un vettore con nome 'V'. Utilizzare completando il seguente frammento di codice main. int main(int argc, char* argv[]) { ... // controllo di argc ed inizializzazione del vettore V con i puntatori a funzione int opt; while ((opt = getopt(argc,argv, "n:m:o:h")) != -1) { switch(opt) { case '?': { } break; default: // invocazione della funzione di gestione passando come parametro l'argomento restituito da getopt if (V[opt%4]( (optarg==NULL ? argv[0] : optarg) ) == -1) { } } } return 0; } ===== Esercizio 3: funzioni rientranti ===== Si consideri il seguente programma: #include #include int main(int argc, char *argv[]) { if (argc != 3) { fprintf(stderr, "use: %s stringa1 stringa2\n", argv[0]); return -1; } char* token1 = strtok(argv[1], " "); while (token1) { printf("%s\n", token1); char* token2 = strtok(argv[2], " "); while(token2) { printf("%s\n", token2); token2 = strtok(NULL, " "); } token1 = strtok(NULL, " "); } return 0; } Riscrivere il programma precedente (che produce un output non corretto) utilizzando la chiamata di libreria ''strtok_r'' al posto di ''strtok''. **NOTA**: se si utilizza lo standard ISO 99 o 11 (-std=c99/11), per evitare i warnings del tipo "implicit declaration of function X" aggiungere la seguente opzione di compilazione "-D_POSIX_C_SOURCE=200112L", oppure dichiarare il prototipo della funzione (copiando la segnatura dal man della funzione), oppure inserire la seguente define **prima del primo include**: #define _POSIX_C_SOURCE 200112L ===== Esercizio 4: numeri random ===== Generare ''N'' numeri casuali interi nell'intervallo ''[K1,K2['' utilizzando la funzione ''rand_r()''. N, K1 e K2 sono passati come argomenti del main opzionali, e se non forniti dall'utente assumono valori di default sulla base di opportune ''#define'' (es. N=1000 K1=100 K2=120). Il programma deve restituire il numero di occorrenze di ciascun intero ''i'' nell'intervallo ''[K1,K2[''e stamparle sullo standard output. Un esempio di output prodotto considerando K1=0 e K2=10, è il seguente: Occorrenze di: 0 : 10.25% 1 : 9.97% 2 : 9.48% 3 : 9.77% 4 : 10.19% 5 : 10.93% 6 : 9.80% 7 : 9.93% 8 : 10.00% 9 : 9.68% ===== Esercizio 5: valgrind ===== Verificare la correttezza degli accessi in memoria utilizzando ''valgrind'' dei programmi realizzati nell'esercizi precedenti. Verificare che non ci siano "memory leaks" all'uscita del programma. Valgrind permette, fra l'altro, di capire se le variabili sono inizializzate prima del loro uso, se accediamo a memoria gia' deallocata o mai allocata o a zone non inizializzate. Passi da eseguire: * compilare il file da verificare con l'opzione ''-g'' per includere le informazioni di debugging (anche se non è strettamente necessario per l'uso di valgrind). * eseguire il programma con ''valgrind'' nel modo seguente : bash$ valgrind ./prova in questo modo, a schermo verranno riportare le infrazioni rilevate nell'accesso alla memoria. Ad esempio, //invalid read// o //invalid write// sono accessi in lettura o scrittura a memoria non allocata o gia' deallocata. Con l'opzione ''--leak-check=full'' (attenzione, davanti a leek c'e' un doppio '-'), valgrind fornirà dettagli per ogni blocco di memoria che non è più raggiungibile o che, pur essendo raggiungibile, non è stato liberato, dando anche l'informazione di dove il blocco è stato allocato.