Dove si sperimenta qualche tool e si approfondiscono alcune caratteristiche del C sequenziale: errno, perror creazione di librerie e generazione di numeri casuali
Seguire l'esempio dei lucidi visti nella lezione di proprocessing, compilazione e linking verificando i vari passi utilizzando il programma esempio presentato ed i comandi descritti nei lucidi presentati a lezione
Realizzare una libreria che fornisce le operazioni su liste implementate negli esercizi 2 e 3 dell'esercitazione 1.
In C tipicamente per realizzare una libreria si difinisce un file .h con i prototipi ed i tipi messi a disposizione dalla libreria e si fornisce il codice pre-compilato in un opportuno file di libreria .a.
Nel nostro caso l'header si chiamera' lista.h e la libreria si chiamera' libList.a.
Procedere nel modo seguente:
lista.h che contiene i tipi ed i prototipi delle funzioni implementate lista.c che contiene il codice che implementa le funzioni.lista.o da inserire nella libreria con il comando gcc come segue$ gcc -Wall -pedantic -c lista.c
ar come segue$ ar -r libList.a lista.o
Quando la creazione e' andata a buon fine creare un file main.c che contenga uno dei main di test sviluppati negli esercizi precedenti e compilarlo utilizzando la libreria. In particolare :
lista.h$ gcc -Wall -pedantic main.c -lList -L.
dove l'opzione -l indica il nome della libreria da usare in fase di linking e l'opzione -L specifica la directory dove cercarla (oltre alle directory standard tipo /usr/lib).
.
In C, la maggior parte delle funzioni di libreria che segnalano un errore settano anche la variabile globale errno con dei codici definiti da diversi standard. I codici sono valori interi, definiti da opportune macro. Per vadere il loro valore eseguire
bash$ man errno
Dopo l'esecuzione di una funzione di libreria che imposta errno e' possibile chiamare la funzione di libreria perror() che ispeziona il valore di errno e trasforma il valore numerico in un messaggio testuale comprensibile all'utente (vedere man perror per il suo uso).
E' possibile manipolare errno da programma includendo l'header errno.h con
#include <errno.h>
L'esercizio richiede di essegnare a errno i valori EINTR EPERM EBUSY e stampare i corrispondenti messaggi di errore usando perror().
Generare N numeri casuali interi nell'intervallo [0,K-1] utilizzando le funzioni rand() ed srand(). N e K sono definiti con delle opportune #define. Per N==100 e K==4 eseguire quanto segue.
c_i di ciascun intero i nell'intervallo [0,K-1]e stamparle sullo standard outputi calcolare la percentuale di occorrenze sulle occorrenze totali (c_i diviso N) e stamparle sullo standard output
Ripetere l'esecizio 4 utilizzando la funzione rand_r(). A che serve questa funzione ? Quali sono le principali differenze ? (Suggerimento: partire dall'analisi accurata del man)
Verificare la correttezza degli accessi ai puntatori compiuti dalle funzioni su liste di interi della libreria libList.a utilizzando valgrind.
Questo strumento permette fra l'altro di capire se tutte le variabili sono inizializzate prima del loro uso, se accediamo a memoria gia' deallocata o mai allocata e situazioni similari
Per fare questo procedere come segue:
-g per includere le informazioni di debugging. Ad esempio se il mio file si chiama main.c posso compilare conbash$ gcc -Wall -pedantic -g -o prova main.c
bash$ valgrind ./prova
in questo modo, a schermo verranno riportare le infrazioni rilevate. Ad esempio, invalid read o invalid write sono accessi in lettura o scrittura a memoria non allocata o gia' deallocata.
1) Compilare ed eseguire il seguente programma:
#include <stdio.h> #include <math.h> int main (void) { double x=3.0; printf("Radice = %f\n",sqrt(x)); return 0; }
salvato nel file ff.c con
gcc -Wall -pedantic ff.c
Chi segnala un errore? E' fallita la fase di preprocessing, la compilazione o il linking? Cosa contine il modulo oggetto se specifico l'opzione -c? Come si risolve il problema?
2) Cosa accade se eliminiamo la linea
#include <math.h>
? A questo punto cosa va storto? Sapete interpretare i messaggi a video e stabilire chi li ha scritti e perche'? Viene generato l'eseguibile?
3) Generare il modulo oggetto con
gcc -Wall -pedantic -c ff.c
Utilizzare objdump, nm, readelf per capire cosa contengono la tabella di rilocazione, la tabella dei simboli esportati ed esterni, le sezioni data, BSS e codice. (utilizzare il man e cercare su google).
4) Usare l'opzione -E e la -S del gcc: che cosa succede? Cosa accade specificando il flag -g assieme a -S?
Usare le macro con parametri per definire una macro che somma (operatore +) i propri argomenti
#define SOMMA(X,Y,Z) ......
e testarla in un opportuno main. Valutare le differenze con una funzione di prototipo
int SOMMA(int X,int Y, int Z);
Scrivere una macro con parametri che calcoli il fattoriale di un numero N, passato come parametro e ne stampi il risultato. Ad esempio, posso utilizzare la macro per calcolare il fattoriale di 4+1 con
FATTORIALE(4+1)
La macro non deve fare assunzioni su come verranno passati i parametri. Che accade annidando due chiamate della macro? Ad esempio
FATTORIALE(FATTORIALE(4+1))