FUNZIONI DI BIBLIOTECA I programmi C hanno accesso a un insieme preprogrammato standard di funzioni per: calcolare quantità matematiche, gestire l’ingresso/uscita dei caratteri, manipolare stringhe di caratteri. Queste funzioni preprogrammate sono memorizate in una biblioteca di sistema che contiene una collezione di funzioni standard e collaudate. Prima di usare una funzione disponibile nella biblioteca di sistema si deve conoscere: il nome della funzione; gli argomenti richiesti dalla funzione; il tipo dati del risultato (se presente) restituito dalla funzione; una descrizione di ciò che fa la funzione. I primi tre dati sono forniti dalla intestazione della funzione. Ad es., consideriamo la funzione sqrt(), che calcola la radice quadrata del suo argomento. La sua intestazione è: double sqrt(double num) Essa contiene tutte le informazioni necessarie per la chiamata a sqrt(), che si aspetta un argomento in doppia precisione e restituisce un valore pure in doppia precisione. Per funzionare correttamente, molte funzioni di biblioteca richiedono un insieme standard di dichiarazioni comuni e altre informazioni, contenute in un file d’intestazione standard. Per inserire in un programma le informazioni di questo file, il programma deve cominciare con la seguente istruzione: #include <nome-file-intestazione> (che termina senza punto e virgola). Funzioni matematiche Per eseguire calcoli quali la potenza, la radice quadrata, il valore assoluto di un numero e altri, ogni compilatore C fornisce un gruppo standard di funzioni matematiche preprogrammate. Le più comuni sono elencate in tabella (dove i tipi non dichiarati si intendono double). Il differente comportamento delle tre funzioni ceil(), floor() e (int)()può essere osservato eseguendo il seguente programma: #include <stdio.h> #include <math.h> main() { printf("ceil(3.1) = printf("floor(3.1) = printf("(int)(3.1) = printf("ceil(-3.1) = printf("floor(-3.1)= printf("(int)(-3.1)= } %f %f %d %f %f %d ceil(3.9) = floor(3.9) = (int) (3.9)= ceil(-3.9) = floor(-3.9)= (int)(-3.9)= che produce la seguente uscita: %f\n",ceil(3.1),ceil(3.9)); %f\n",floor(3.1),floor(3.9)); %d\n\n",(int)(3.1),(int)(3.9)); %f\n",ceil(-3.1),ceil(-3.9)); %f\n",floor(-3.1),floor(-3.9)); %d",(int)(-3.1),(int)(-3.9)); Come con tutte le funzioni C, gli argomenti passati a una funzione di biblioteca matematica non devono essere necessariamente numeri, ma possono anche essere espressioni, come nei seguenti esempi: sqrt(4.0 + 7*3) abs(-24 % 3 + 6) pow(p * q, 5.0) Anche ora vengono dapprima valutate le espressioni in parentesi, quindi vengono forniti i risultati. Esempi: Come le funzioni scritte dall’utente, anche quelle di biblioteca possono essere inserite come parte di espressioni più ampie. Il valore restituito dalla funzione viene valutato prima che si compia qualsiasi altra operazione, e può essere usato come argomento di un’altra funzione. Ad es., è valida un’espressione del tipo: sqrt(abs(alfa)) dove l’elaborazione procede dalla coppia di parentesi interna a quella esterna. Esercizio. Scrivere un programma che calcoli il tempo impiegato da una pallina per toccare terra dopo che sia stata lasciata cadere da un edificio. Suggerimento: impiegare la seguente formula per calcolare il tempo [in secondi] impiegato per percorere una data distanza [in metri]: tempo = sqrt(2*distanza/g) dove g è l’accelerazione di gravità 9,8 m/sec2. #include <stdio.h> #define GRAV 9.8 void main(void) { double tempo, distanza; double sqrt(double); /* dichiarazione */ printf("Scrivi la distanza (in m): "); scanf("%lf", &distanza); tempo = sqrt(2 * distanza / GRAV); printf("\nImpiegherà %4.2lf secondi", tempo); printf("\nper cadere da %8.3lf metri.", distanza); } File math.h. Osserviamo che il programma precedente contiene, dopo l’istruzione di dichiarazione delle variabili, il prototipo della funzione sqrt(). In alternativa, tutti i compilatori possiedono un file standard di nome math.h che contiene gli opprtuni prototipi di funzione per le funzioni matematiche fornite. Le informazioni contenute in questo file consentono di usare tutte le funzioni matematiche senza scrivere esplicitamente le istruzioni prototipi di ogni funzione, e sono disponibili inserendo nel programma la seguente istruzione al preprocessore: #include <math.h> In tal modo il programma precedente si può riscrivere come: #include <stdio.h> #include <math.h> #define GRAV 9.8 void main(void) { double tempo, distanza; printf("Scrivi la distanza (in m): "); scanf("%lf", &distanza); tempo = sqrt(2 * distanza / GRAV); printf("\nImpiegherà %4.2lf secondi", tempo); printf("\nper cadere da %8.3lf metri.", distanza); } In esso non è presente l’istruzione di dichiarazione double sqrt(double); del programma precedente. Funzione rand(). La funzione rand() genera un intero compreso tra 0 e RAND_MAX, una costante simbolica definita nel file d’intestazione stdlib.h. Lo standard ANSI stabilisce che il valore di RAND_MAX debba essere almeno 32767, che è il valore massimo per un intero di 2 byte (ossia di 16 bit). Il protoptipo per la funzione rand() si trova nel file d’intestazione stdlib.h. Esso contiene i prototipi delle funzioni per la conversione dei numeri in testo e viceversa, per l’allocazione della memoria, per i numeri casuali e per altre funzioni di utilità generica. Tuttavia, l’intervallo dei valori necessario in una applicazione specifica potrebbe essere differente da quello dei valori prodotti da rand(). Ad es., un programma che simuli il lancio di una moneta richiederebbe soltanto 0 per “testa” e 1 per “croce”, mentre uno che simuli il lancio di un dado a 6 facce richiederebbe degli interi compresi nell’intervallo da 1 a 6. Esempio: programma 20 lanci. Per mostrare l’utilizzo di rand(), sviluppiamo un programma che simuli 20 lanci di un dado a 6 facce, visualizzando il valore ottenuto in ogni lancio. Dapprima generiamo dei numeri casuali compresi nell’intervallo da 0 a 5 utilizzando l’operatore resto (%) in congiunzione con rand() nel modo seguente: rand() % 6 Questa operazione si chiama riduzione in scala, mentre il numero 6 è detto fattore di scala. Quindi “trasliamo” di una unità l’intervallo dei numeri generati, aggiungendo 1 al risultato precedente. Se vogliamo stampare i numeri in forma di tabella a 5 colonne, possiamo inserire un’istruzione di salto a riga nuova (carattere di escape \n) dopo la visualizzazione di ciascun gruppo di 5 risultati. Il programma 20 lanci completo è il seguente: #include <stdio.h> #include <stdlib.h> int main() { int i; for (i=1; i<=20; i++) { printf("%10d", (rand() % 6) + 1); if (i % 5 == 0) { printf("\n"); } } } Programma 6000 lanci. Il precedente programma può essere modificato per verificare la “bontà” del generatore di numeri casuali, simulando un numero elevato (6000) di lanci, e calcolando la frequenza con la quale si presentano le diverse facce. Se i lanci fossero completamente casuali, ciascuna faccia si dovrebbe presentare più o meno lo stesso numero di volte (in questo caso 1000). #include <stdio.h> #include <stdlib.h> int main() { int freq1 = 0; int freq2 = 0; int freq3 = 0; int freq4 = 0; int freq5 = 0; int freq6 = 0; int lanci, faccia; for (lanci = 1; lanci <= 6000; lanci++) { faccia = 1 + rand() % 6; switch (faccia) { case 1: ++freq1; break; case 2: ++freq2; break; case 3: ++freq3; break; case 4: ++freq4; break; case 5: ++freq5; break; case 6: ++freq6; break; } } printf("%s printf(" printf(" printf(" printf(" printf(" printf(" } Ecco l’uscita prodotta: %13s\n", "Faccia Frequenza"); 1 %10d\n", freq1); 2 %10d\n", freq2); 3 %10d\n", freq3); 4 %10d\n", freq4); 5 %10d\n", freq5); 6 %10d\n", freq6); Osserviamo che: • il programma non contiene alcun caso di default nel comando switch; • l’intero comando switch può essere sostituito con un’istruzione di una sola riga, usando un vettore, come vedremo dopo avere introdotto la funzione srand(); • se i due programmi precedenti vengono eseguiti ripetutamente, essi producono sempre la stessa sequenza di numeri (e infatti si dice che la funzione rand() genera dei numeri pseudocasuali). Funzione srand(). Per modificare i due programmi precedenti in modo che producano ogni volta una diversa sequenza di numeri casuali (operazione detta randomizzazione), si usa la funzione di biblioteca standard srand(), il cui prototipo è contenuto anch’esso nel file stdlib.h. srand() riceve un argomento intero unsigned con il quale insemina la funzione rand(), in modo da indurla a generare una diversa sequenza di numeri casuali a ogni esecuzione del programma. Ricordiamo che anche il tipo dati unsigned (abbreviazione per unsigned int) è immagazzinato in 2 byte di memoria, ma può contenere solo valori positivi compresi nell’intervallo da 0 a 65535. Ricordiamo anche che per leggere un valore unsigned con la funzione scanf() si usa la specifica di conversione %u. Il programma 20 lanci modificato è il seguente: #include <stdio.h> #include <stdlib.h> int main() { int i; for (i=1; i<=20; i++) { printf("%10d", (rand()%6)+1); if (i % 5 == 0) { printf("\n"); } } } #include <stdio.h> #include <stdlib.h> int main() { int i; unsigned seme; printf(“Scrivi il seme: “); scanf(“%u”, &seme); srand(seme); for (i = 1; i <= 20; i++) { printf("%10d",(rand()%6)+ 1); if (i % 5 == 0) { printf("\n"); } } } Qualora si volesse randomizzare senza dovere immettere ogni volta un seme, si potrebbe utilizzare un’istruzione del tipo: srand(time(NULL)); Essa indurrebbe il computer a leggere il suo clock interno per ottenere automaticamente un valore per il seme. La funzione time restituisce l’ora corrente espressa in secondi. Questo valore viene convertito in un intero senza segno e utilizzato come seme per il generatore di numeri casuali. Il prototipo per la funzione time è nel file di biblioteca time.h. La versione definitiva del programma 20 lanci è quindi: #include <stdio.h> #include <stdlib.h> #include <time.h> int main() { int i; srand(time(NULL)); for (i = 1; i <= 20; i++) { printf("%10d", (rand() % 6) + 1); if (i % 5 == 0) { printf("\n"); } } } Ecco ora la versione con vettore del programma 6000 lanci. #include <stdio.h> #include <stdlib.h> #include <time.h> #define DIMENS 7 int main() { int faccia, lanci, frequenza[DIMENS] = {0}; srand(time(NULL)); for (lanci = 1; lanci <= 6000; lanci++) { faccia = rand() % 6 + 1; ++frequenza[faccia]; } printf("%s \n", "Faccia Frequenza"); for (faccia = 1; faccia < DIMENS; faccia++) { printf("%4d %10d \n", faccia, frequenza[faccia]); } } Ed ecco le uscite prodotte: