Next: La rappresentazione dei numeri Up: Prerequisiti e richiami tecnici Previous: Principali comandi di Emacs


Il compilatore C

Per compilare si usa il comando

 
        gcc nomefile.c -o nomefile -Wall -lm

che crea l'output sul file nomefile. Se non si mette l'opzione ``-o nomefile'' l'eseguibile creato si chiama per default a.out.

L'opzione ``-lm'' serve a far includere dal linker la libreria con le funzioni matematiche come sin, exp, sqrt. Dimenticandola si possono ricevere errori da parte del linker, che non trova la definizione di queste funzioni (indipendentemente dal fatto che sia stato incluso math.h, che ne contiene solo le dichiarazioni).

L'opzione ``-Wall'' può fare risparmiare ore di caccia ai bug, perché vi avvisa di molti costrutti sospetti, che in genere si rivelano essere dei veri e propri errori. Dimenticandola il compilatore non vi avvisa, ad esempio, se usate una funzione dal tipo mai dichiarato. Vi raccomando vivamente di usarla sempre, eventualmente creandovi un alias per evitare di riscriverla (basta scrivere ``alias gcc gcc -Wall'' da shell o metterlo nel proprio .tcshrc).

Abituatevi ad inserire all'inizio del sorgente C le linee

 
        #include <stdio.h>
        #include <stdlib.h>
        #include <math.h>

a definire main come

 
        int main(int argc, char *argv[])

e a concludere l'esecuzione del programma con un'istruzione ``return 0'' (dimenticarlo crea problemi se, ad esempio, il programma è incluso in uno script, perché la shell crederà che l'esecuzione è fallita per qualche ragione).

Il file stdio.h contiene le dichiarazioni delle funzioni standard di input e output (che si trovano nella libreria standard libc). Il file stdlib.h contiene molte altre dichiarazioni, tra cui quelle per la gestione della memoria e per i numeri pseudocasuali (anche queste stanno in libc). Il file math.h contiene le dichiarazioni di funzioni matematiche (che stanno nella libreria libm): è importante includerlo altrimenti il compilatore assume, per default, che funzioni come pow o sqrt restituiscano valori interi (e questo causa errori disastrosi a run time).

Vediamo ora un elenco di errori tipici fatti da chi non ha molta dimestichezza con il C.

·         leggere variabili dall'input utilizzando formati sbagliati. Per leggere usare sempre %f per i float e %lf per i double, e non dimenticare il & quando si usa l'istruzione ``scanf("%lf",&x)''. Sbagliare il formato o dimenticare il & causa in genere un bel Segmentation fault;

·         stampare variabili usando un formato sbagliato. Usare il formato %f o %g indifferentemente per float o double (spesso per sbaglio si mette %d); se si vogliono stampare molte cifre si può usare il formato %.15f (stampa 15 cifre dopo la virgola). Per ulteriori dettagli consultare la pagina di manuale di fprintf;

·         usare un puntatore senza avergli allocato la memoria. Il miglior modo per allocare la memoria per un oggetto è ``p=calloc(1, sizeof *p)''. Se p deve puntare ad un array di k elementi, allora ``p=calloc(k, sizeof *p)''. Ricordarsi che gli elementi sono p[0],...,p[k-1], in particolare l'elemento p[k] non esiste. Utilizzando calloc la memoria viene inizializzata a 0;

·         non liberare mai la memoria utilizzata. Quando una zona di memoria non serve più, la memoria va liberata con free(p). Per evitare di utilizzarla successivamente senza accorgersene, è buona norma fare p=0 subito dopo la free: così un eventuale uso causerà errore a run-time e verrà scoperto più facilmente.

·         confrontare valori in floating point come se fossero esatti. Ad esempio, se a è un float non si deve scrivere ``if (a==0.)'' ma ``if (fabs(a)<SMALL)'', dopo aver definito SMALL come un multiplo opportuno della precisione di macchina. In modo analogo vanno aggiustati gli altri tipi di confronto (``a==b'' diventa ``fabs(a-b)<SMALL'').

·         dimenticarsi di includere math.h, col risultato che funzioni come tan o pow risultano (per il compilatore) prendere argomenti interi e restituire valori interi. Di solito il programma si pianta con Segmentation fault;

·         generare valori pseudocasuali con rand(), che restituisce sempre valori interi (compresi tra 0 e MAX_INT). Per generare dei float si usa drand48(), che restituisce valori tra 0 e 1. Attenzione al fatto ad ogni esecuzione del programma i valori generati sono sempre uguali, se non si cambia seme esplicitamente. Per inizializzare il seme si può fare srand48(time(0)), dopo aver incluso time.h.

·         ... e tanti altri ancora che scoprirete da soli.


Next: La rappresentazione dei numeri Up: Prerequisiti e richiami tecnici Previous: Principali comandi di Emacs

Daniele Finocchiaro
1998-11-13