Strumenti Utente

Strumenti Sito


informatica:sol:laboratorio15:esercitazionib:esercitazione3

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 <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?

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 <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#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<N)
    {
 
      a[++i]=rand()%2;
      printf("iterazione %d\n",i);
    }
  printf("exiting ...\n");
  return EXIT_SUCCESS;
}

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' ?

informatica/sol/laboratorio15/esercitazionib/esercitazione3.txt · Ultima modifica: 03/03/2015 alle 17:03 (8 anni fa) da Susanna Pelagatti