Traccia della lezione Lez03 Lucido 3: I codici LOCAL1.C, LOCAL2.C e LOCAL3.C illustrano il concetto di variabile locale a un blocco. Tutti e tre hanno un blocco sequenziale (senza condizioni né cicli) annidato nel main. E' una struttura rarissima nell'uso pratico, e che non è consigliabile usare; la consideriamo qui solo per capire come funziona. Si noti la funzione StampaIntero, che serve a stampare a video un numero intero, ha un dato (il numero da stampare) e nessun risultato. Si noti che: - LOCAL1.C non compila, perché la variabile j è usata nel main, ma dichiarata nel blocco interno. - LOCAL2.C compila, ma la stampa della variabile j è sbagliata perché j non è veramente inizializzata: l'istruzione j = 0 inizializza una seconda variabile j dichiarata nel blocco interno, che è una variabile diversa, benché abbia lo stesso nome - LOCAL3.C compila e dà il risultato atteso Lucido 4: Lo standard C consente di mescolare la parte dichiarativa ed esecutiva di un blocco, purché ogni variabile venga dichiarata prima di essere usata. Questo è sconsigliato, perché rende il codice meno leggibile. Capita di sentire che una variabile è usata troppo lontano da dove è stata dichiarata: in genere significa che il blocco è troppo lungo, e va diviso. Lo standard C consente di dichiarare variabili entro blocchi che non siano funzioni. Questo è poco consigliabile, perché rende il codice meno leggibile. Se si sente il bisogno di farlo, in genere significa che il blocco merita di diventare una funzione. Mentre si programma, le sensazioni istintive meritano ascolto e giudizio. Lucido 5: Inizializzare le variabili nella stessa istruzione che le dichiara aiuta a garantire che abbiano sempre un valore sensato, ma il codice è più chiaro se invece si inizializzano le variabili subito prima di usarle nei calcoli. La regola secondo cui in un assegnamento variabile e valore devono essere dello stesso tipo ha in realtà molte eccezioni. Vedremo che esistono regole implicite di conversione, che trasformano il valore nel tipo corretto prima di assegnarlo alla variabile. Di queste conversioni ci occuperemo in una lezione ad hoc. E' però sempre bene evitarle o, se sono necessarie, renderle esplicite. Alcuni testi chiamano "rvalue" (right-hand-side value) tutto ciò che può stare a destra di un operatore di assegnamento, cioè costanti, variabili, chiamate a funzione ed espressioni composte. Chiamano anche "lvalue" (left-hand-side value) tutto ciò che può stare a sinistra di un operatore di assegnamento. Per ora, conosciamo solo le variabili, ma vedremo in seguito altri oggetti più sofisticati. Lucido 7: Il codice EXPRESSION.C illustra diversi tipi di espressioni (costanti esplicite e simboliche, variabili, chiamate a funzione ed espressioni composte). Si noti la funzione CalcolaSomma, che riceve due interi come dati e restituisce un intero come risultato. Lucido 10: Con "priorità e associatività consuete" si intende quelle dell'algebra: * gli operatori unari precedono prodotto, divisione e resto. * prodotto, divisione e resto precedono somma e differenza Tutti gli operatori sono associativi a sinistra, tranne quelli unari che lo sono a destra: 2 + 3 + 4 significa ((2 + 3) + 4) + - 2 significa + (-2) Un esercizio sulla valutazione di espressioni aritmetiche: 2 * 3 + 5 + 4 * 8 * 2 (2*3) + 5 + 4 * 8 * 2 (2*3) + 5 + (4*8) * 2 (2*3) + 5 + ((4*8)*2) ((2*3)+5) + ((4*8)*2) (((2*3)+5)+((4*8)*2)) e fa 6 + 5 + 4 * 8 * 2 6 + 5 + 32 * 2 6 + 5 + 64 11 + 64 75 Lucido 12: Un esercizio sulla valutazione di espressioni relazionali e logiche. Supponendo che sia a = 1; b = 1; c = 1; si valuti la seguente espressione: a < 10 && 2 * b < c a < 10 && (2*b) < c (a < 10) && (2*b) < c (a < 10) && ((2*b) < c) ((a < 10) && ((2*b) < c)) Quindi: 1 < 10 && 2 * 1 < 1 1 < 10 && 2 < 1 true && 2 < 1 true && ( 2 < 1) true && false false Lucido 14: Il fatto che in realta' i valori logici siano valori interi ha conseguenze complesse che indagheremo nella prossima lezione. Una prima conseguenza ovvia, però, è che in realtà è possibile sottoporre valori logici a operatori aritmetici e di relazione, che li tratteranno come se fossero numeri. Ovviamente, questo richiede l'uso delle parentesi, per forzare le regole di priorità standard. Per es., se b1 = 1 (vero) e b2 = 0 (falso), si può valutare l'espressione ( (b1 || b2) < b1 ) + 7 (b1 || b2) vale vero, cioè 1 ( (b1 || b2) < b1 ) vale 1 < 1, cioè falso, cioè 0 ( (b1 || b2) < b1 ) + 7 vale 7 Peggio ancora, è possibile scrivere espressioni che sembrano avere un significato, e ne hanno un altro: int i = 3; (10 < i < 6) vale vero Infatti, (10 < i) vale falso, cioè 0 e (0 < 6) vale vero.