Traccia della lezione Lez17: Lucido 3: La direttiva #include deve comparire al principio di tutti i file di codice (.C ) nei quali si usano funzioni dichiarate nella libreria da includere. Si può includerla indirettamente attraverso un altro file header (.H). Per convenzione, il nome della libreria viene racchiuso fra < e > (<...>) nel caso di librerie standard, fra virgolette ("...") nel caso di librerie acquistate altrove o scritte dall'utente. Lucido 4: Il codice PROVA_ASSERT.C calcola il rapporto fra "i+1" e "i" per "i" che cala da 10 a 0. Si noti l'uso del cast per eseguire la divisione esatta anziché troncata. L'istruzione assert(i != 0); verifica che "i" sia diverso da 0. Per i compreso fra 10 e 1, tutto funziona correttamente; per i = 0, il programma si arresta. Ovviamente, in questo codice semplicissimo l'errore era ovvio; in un codice più complicato, la verifica di valori inaccettabili può non essere ovvia. In fase di scrittura del codice, il controllo fornito da assert può aiutare a trovare errori insidiosi. Al termine dello sviluppo, le istruzioni assert potranno essere cancellate per accelerare l'esecuzione. Il codice PROVA_CTYPE.C scorre i caratteri da CHAR_MIN a CHAR_MAX e determina per ciascuno se si tratta di una cifra, un segno punteggiatura, un carattere alfanumerico o alfabetico. Nel caso dei caratteri alfabetici, determina se sia maiuscolo o minuscolo e stampa il corrispondente carattere dell'altro tipo. Si noti che il ciclo non arriva fino a CHAR_MAX perché l'istruzione c++ al termine del ciclo farebbe uscire c dall'insieme di definizione, riportandolo al valore CHAR_MIN, e quindi provocando un ciclo infinito. La cosa viene però segnalata con un avvertimento dal compilatore. Sulla mia macchina diversi caratteri vengono definiti alfabetici pur non essendolo, e viceversa, ma non so perché. Lucido 5: Il codice PROVA_MATH.C calcola per n = -2.5 il valore del seno, dell'esponenziale (a base e), dell'arrotondamento per difetto e per eccesso (si noti che il risultato è un numero reale, non intero!), del valore assoluto (fabs() perché è reale, per gli interi si usa abs()), dei logaritmi naturale e in base 10 (ovviamente applicati sul valore assoluto), della radice quadrata (ovviamente applicata sul valore assoluto) e della potenza di n di esponente fabs(n). Si noti che alcune di queste operazioni hanno risultato indeterminato se vengono eseguite su n anziché sul valore assoluto. Lucido 7: Il codice PROVA_STDLIB.C effettua una chiamata al sistema operativo attraverso l'istruzione "system". L'argomento di tale funzione è una stringa che contiene un comando che verrà eseguito come se ci si trovasse in una finestra terminale nella directory dove sta l'eseguibile del programma PROVA_STDLIB.EXE. L'istruzione exit() termina il programma e restituisce al sistema operativo il valore numerico del suo argomento, che è un numero intero. Per i programmi terminati correttamente si usa la costante simbolica EXIT_SUCCESS, che tipicamente vale 0, mentre si usano valori diversi se il programma termina a causa di errori (ad esempio per allocazioni o aperture di file fallite). I valori usati vanno dichiarati come costanti con la direttiva #define. Tale valore numerico può essere letto da programmi esterni che lanciano a loro volta l'eseguibile PROVA_STDLIB.EXE. Ad esempio, il file batch PROVA_EXIT.BAT lancia l'eseguibile PROVA_STDLIB.EXE e ne verifica il risultato stampando un messaggio. Si noti che il comando IF ERRORLEVEL n, dove n è un numero intero, va interpretato così: "Recupera il risultato dell'ultimo programma lanciato e confrontalo con n. Se il risultato (ERRORLEVEL) è >= n, allora esegui l'istruzione seguente." Il confronto è un >=, e quindi l'eseguibile PROVA_STDLIB.EXE nella sua versione originale esegue tutti i tre i comandi. Cancellando l'istruzione exit, si può vedere come il messaggio del file batch cambi in corrispondenza: viene eseguito solo il primo comando, perché il programma restituisce 0. Cambiando il valore della costante simbolica EXIT_INTERRUPT da 2 a 1, invece, se ne eseguono due. Lucido 8: Il codice PROVA_TIME.C usa la funzione clock() per determinare il tempo trascorso dall'inizio del programma. L'unità di misura è determinata dalla costante CLOCKS_PER_SEC, che dice quante unità ci sono in un secondo. Poi usa la funzione time() per determinare i secondi trascorsi dal 01/01/1970. Si tratta di approssimativamente 1 miliardo e 300 milioni di secondi: 80000 al giorno * 365 giorni * 40 anni. Entrambe le funzioni restituiscono oggetti di un tipo specifico, definito nella libreria time.h, che però in fin dei conti è un intero a 32 bit. La funzione difftime(t2,t1) dà la differenza in secondi fra i due valori, e quindi i secondi trascorsi fra l'uno e l'altro, espressa come double. Il risultato della differenza tra due valori di clock() è quasi identico, ma vi sono due differenze: 1) la differenza di due clock() è espressa in millisecondi o microsecondi (dipende dalla macchina: tipicamente, dipende dalla distinzione tra 32 bit o 64 bit, e corrisponde a due diversi valori della costante CLOCKS_PER_SEC, che è un double) 2) se ci sono più programmi in esecuzione in parallelo, time() misura il tempo assoluto, mentre clock() quello trascorso a eseguire il singolo programma. Quindi per sapere quanto tempo impiega un programma, occorre usare clock(). Siccome il risultato di clock() è espresso da interi a 32 bit, sulle macchine in cui il tempo è misurato in microsecondi, si rischia facilmente l'overflow: infatti, 2^31 microsecondi, cioè circa 2 miliardi di microsecondi sono 2000 secondi, cioè poco più di mezz'ora. Esecuzioni più lunghe di tale durata vanno soggette a overflow. Per ovviare, si può registrare il tempo con entrambe le procedure e correggere il risultato di clock() per confronto con quello di time().