Strumenti Utente

Strumenti Sito


informatica:sol:laboratorio16:esercitazionib:esercitazione2

Esercitazione 2

Dove si sperimenta qualche tool e si approfondiscono alcune caratteristiche del C sequenziale: errno, perror creazione di librerie e generazione di numeri casuali

Esercizio 1: Preprocessing, compilazione e linking

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

Esercizio 2: Creare ed usare una libreria di liste

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:

  • 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'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). .

Esercizio 3: Manipolare ''errno'' e uso di ''perror()''

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().

Esercizio 4: Generazione numeri casuali con ''rand()'' ed ''srand()''

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.

  • Calcolare il numero di occorrenze c_i di ciascun intero i nell'intervallo [0,K-1]e stamparle sullo standard output
  • Per ogni i calcolare la percentuale di occorrenze sulle occorrenze totali (c_i diviso N) e stamparle sullo standard output
  • Esaminare le occorrenze degli interi nell'intervallo e verificare che sono piuttosto vicine fra di loro
  • Che distribuzione hano i numeri generati ?

Esercizio 5: Generazione numeri casuali con ''rand_r()''

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)

Esercizio 6: Verificare gli accessi in memoria: valgrind

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:

  • compilare il file da verificare con opzione -g per includere le informazioni di debugging. Ad esempio se il mio file si chiama main.c posso compilare con
bash$ gcc -Wall -pedantic -g -o prova main.c
  • eseguire
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.

Esercizio 7: 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 8: 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 9: 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)) 
informatica/sol/laboratorio16/esercitazionib/esercitazione2.txt · Ultima modifica: 03/03/2016 alle 08:23 (9 anni fa) da Susanna Pelagatti