Next: La rappresentazione dei numeri Up: Prerequisiti
e richiami tecnici Previous: Principali
comandi di Emacs
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