−Indice
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 <num. intero> -m <num. intero> -o <stringa> -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 '?': { <gestione parametro non riconosciuto> } 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) { <gestione errore> } } } return 0; }
Esercizio 3: funzioni rientranti
Si consideri il seguente programma:
#include <stdio.h> #include <string.h> 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.