====== Esercitazione 3 ======
Dove si prova a creare una libreria, si approfondisce la conoscenza delle fasi di preprocessing, compilazione e linking, si prova ad implementare delle liste generiche in C e si studiano piu' approfonditamente le caratteristiche dei ''valgrind'' ed alcune opzioni di ''gcc''. Rassegnadoci all'idea che un programma che passa tutti test puo' nondimeno essere scorretto ....
===== Esercizio 1: Libreria di liste =====
Realizzare una libreria che fornisce le operazioni su liste implementate negli esercizi 2 e 3.
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:
* Definire un file ''lista.h'' che contiene i tipi ed i prototipi delle funzioni implementate
* Definire un file ''lista.c'' che contiene il codice che implementa le funzioni.
* Generare il modulo oggetto ''lista.o'' da inserire nella libreria con il comando ''gcc'' come segue
$ gcc -Wall -pedantic -c lista.c
* Creare la libreria con i lcomando ''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 :
* il main di test dovra' includere il file ''lista.h''
* in fase di compilazione utilizzare la libreria sviluppata con
$ gcc -Wall -pedantic main.c -lList -L.
dove l'pzione ''-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'').
.
===== Esercizio 2: Ancora su -- Preprocessing, compilazione e linking =====
1) Compilare ed eseguire il seguente programma:
#include
#include
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
? 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?
===== Esercizio 3: Macro con parametri, macro SOMMA =====
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);
===== Esercizio 4: Macro con parametri, macro FATTORIALE =====
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))
===== Esercizio 5. Liste generiche in C =====
In questo esercizio si richiede di realizzare alcune funzioni che lavorano su liste generiche in C. Una lista generica e' rappresentata con la seguenti struct
typedef struct elem {
/** chiave */
void * key;
/** informazione */
struct elem * next;
} elem_t;
typedef struct {
/** la testa della lista */
elem_t * head;
/** la funzione per confrontare due chiavi */
int (* compare) (void *, void *);
/** la funzione per copiare una chiave */
void * (* copyk) (void *);
} list_t;
la prima struttura (''elem_t'') rappresenta un nodo della lista generica. Ogni nodo contiene una chiave ''key'' che puo' avere tipo qualsiasi.
La seconda struttura (''list_t'') permette di definire una particolare lista a partire da quella generica. Per farlo bisogna fornire due funzioni:
* ''compare'' permette di confrontare due chiavi, ritorna 0 se sono uguali ed un valore diverso da 0 altrimenti
* ''copyk'' crea una copia della chiave (allocando la memoria necessaria) e ritorna il puntatore alla copia (se tutto e' andato bene) o NULL (se si e' verificato un errore)
Si chiede di realizzare le funzioni che permettono di creare/distruggere una lista generica e quelle per inserire ed estrarre un elemento generico.
Realizzare un main di test che prova ad istanziare la lista in due versioni: una prima a valori interi usando le seguenti funzioni per il confronto e la copia:
/** funzione di confronto per lista di interi
\param a puntatore intero da confrontare
\param b puntatore intero da confrontare
\retval 0 se sono uguali
\retval p (p!=0) altrimenti
*/
int compare_int(void *a, void *b) {
int *_a, *_b;
_a = (int *) a;
_b = (int *) b;
return ((*_a) - (*_b));
}
/** funzione di copia di un intero
\param a puntatore intero da copiare
\retval NULL se si sono verificati errori
\retval p puntatore al nuovo intero allocato (alloca memoria)
*/
void * copy_int(void *a) {
int * _a;
if ( ( _a = malloc(sizeof(int) ) ) == NULL ) return NULL;
*_a = * (int * ) a;
return (void *) _a;
}
e una seconda che ha come chiavi stringhe usando analoghe funzioni per la copia ed il confronto.
===== Esercizio 6. Approfondiamo l'uso di valgrind (e alcune opzioni utili di gcc ...) =====
Compilare ed eseguire il codice seguente usando valgrind
#include
#include
#include
#include
#define N 5
int main(void)
{
int * a;
int i;
if ( ( a = malloc(N*sizeof(int))) == NULL )
return EXIT_FAILURE;
srand(time(NULL));
i=0;
while (i
che problemi vengono segnalati ? Perche' ?
Provare a compilare (dopo averlo salvato in file.c) usando le seguenti opzioni di "gcc" che permettono di rilevare altri errori statici:
bash$ gcc -O -pedantic -Wall -Wextra -Wformat=2 -ggdb -o exe file.c
viene segnalato qualcosa ? Perche' ?