Le espressioni in C++ (CAP 4) Indice Le espressioni in C++ : precedenze e associatività degli operatori Alberto Garfagnini e Marco Mazzocco Università degli studi di Padova A.A. 2014/2015 gli operatori aritmetici gli operatori logici e relazionali diseguaglianze in C++ gli operatori di incremento e decremento l operatore condizionale gli operatori booleani per operare sui bit degli interi l operatore sizeof Espressioni in C++ Un espressione in C++ è composta da uno o più operandi e fornisce un risultato una volta valutata Gli operatori si dividono in categorie: operatori unari, come l address-of (&) e dereference (*) agiscono su un unico operando operatori binari, come l uguaglianza (==) e la moltiplicazione (*) che agiscono su due operandi esiste anche un operatore ternario che richiede tre operandi la chiamata a funzione richiede un numero variabile di operandi (da zero ad un numero grande, a seconda della funzione) per comprendere come le espressioni vengono valutate, è necessario capire la precedenza e associatività degli operatori 5 + 10 * 20 / 2 per poter determinare il risultato dell espressione è necessario studiare come si comportano gli operatori che la compongono Ordine di valutazione la precedenza definisce come sono raggruppati gli operandi di un espressione complessa nulla è specificato riguardo all ordine di valutazione int i = sqrt(val) * f2(); le due funzioni saranno invocate prima della moltiplicazione, ma non c e nessun modo di conoscere l ordine di chiamata int i = 0; cout << i << " " << ++i << endl; non c e nessun modo per conoscere come verrà valutata l istruzione : A Il compilatore può valutare prima ++i e poi i producendo come output 1 1 B Il compilatore può valutare prima i e in tal caso avremo come output 0 1 C Il compilatore può decidere un altra strategia a noi sconosciuta
precedenza Gli operatori aritmetici Associativity Operator Function Use Left + unary plus + expr Left - unary minus - expr Left * multiplication expr * expr Left / division expr / expr Left % reminder expr % expr Left + addition expr + expr Left - subtraction expr - expr Gli operatori matematici divisione (/) e modulo (%) Il risultato della divisione dipende dal tipo degli operandi int / int 9 / 5 esegue int division 1 double / double 9.0 / 5.0 esegue double division 1.8 long / long 9L / 5L esegue long division 1 float / float 9.0F / 5.0F esegue float division 1.8 hanno tutti precedenza LEFT RIGHT: 5 + 10 * 20 / 2 = 5 + (10 * 20) / 2 = 5 + 200 / 2 = 5 + (200 / 2) = 5 + 100 = 105 Il modulo (%) ritorna il resto della divisione Può essere usato solo per tipi interi 21 % 6 // risultato = 3 21 % 7 // risultato = 0-21 % -8 // risultato = -5 21 % -5 // risultato = 1 Regola generale: m%(-n) = m%n e (-m)%n = -(m%n) precedenza Gli operatori logici e relazionali Associativity Operator Function Use Right! logical NOT!expr Left < less than expr < expr Left <= less than or equal expr <= expr Left > greater than expr > expr Left >= greater than or equal expr >= expr Left == equality expr == expr Left!= inequality expr!= expr Left && logical AND expr && expr Left logical OR expr expr Gli operatori logici: AND e OR AND è true se entrambi gli operandi sono true, altrimenti è false OR è true se almeno uno dei due è true; false se entrambi false Valutazione SHORT-CIRCUIT : expr1 == false expr1 && expr2 expr1 expr2 expr2 non viene valutata, il risultato è comunque false Gli operatori relazionali <, <=, >, >= si applicano a tutti i tipi aritmetici e ai puntatori Gli operatori logici &&,,! si applicano a tutti i tipi convertibili in bool expr1 == true expr2 non viene valutata, il risultato è comunque true
L operatore logico NOT L operatore di negazione, unario è indicato con il simbolo! Esempi: expr == 0!a!expr (int) 1!(x + 7.7)!(a < b c + d) expr!= 0 L operatore! è diverso dall operatore NOT dell algebra booleana: soltanto nell algebra booleana NOT (NOT a) = a in C/C++ (int) 0!!5 = 1!(!5) =!(0) = 1 Diseguaglianze in C++ In matematica si può scrivere 3 < j < 5 per indicare che la variabile j assume valori compresi tra 3 e 5. Se j=4 3 < j < 5 è true Se j=7 3 < j < 5 è false In C++ 3 < j < 5 è equivalente a (3 < j) < 5 quindi, per j = 7 3 < j < 5 (3 < j) < 5 1 < 5 1 true La maniera corretta per implementare il test è (3 < j) && (j < 5) L espressione è true solo se entrambe le parentesi sono true (3 < j) && (j < 5) true && false => false L operatore di assegnazione È l operatore con la precedenza più bassa di tutti è RIGHT associative : la scrittura: x = y = z = 0; equivale a: (x = (y = (z = 0))); L espressione x == y è molto simile a x = y ma il loro effetto è radicalmente diverso! Errore comune nella scrittura di codice if ( a = 1)... invece di if ( a == 1)... A volte molto difficile da individuare!! Test di eguaglianza Statement di assegnazione Gli operatori incremento e decremento forniscono una notazione convenzionale abbreviata per aggiungere (++) e sottrarre (-) una unità da un oggetto di tipo intero possono essere utilizzati in due forme : prefisso : ++x, --x postfisso : x++, x-- int x = 5; int y = ++x; cout << "x = " << x << "y = " << y << endl; int z = 5; int y = z++; cout << "z = " << z << "y = " << y << endl; incrementa e poi assegna il valore x = 6 e y = 6 Assegna il valore e quindi incrementa z = 6 e y = 5 Usate operatori POSTFISSI soltanto quando strettamente necessario
Uso dell operatore postfisso La versione post-fix degli operatori incremento ++ e decremento - è interessante quando si vuole usare il contenuto di una variabile e incrementarla con una singola espressione compatta Esempio vector<double v;... auto pbeg = v.begin(); // stampa tutti gli elementi fino al primo valore negativo; while (pbeg!= v.end() && *beg >= 0) cout << *pbeg++ << endl; // stampa il valore corrente // e incrementa pbeg L operatore incremento postfisso ritorna il valore dell oggetto e poi incrementa il suo contenuto È un operatore ternario L operatore condizionale Sintassi: expression1? expression2 : expression 3 Funzionamento: Se expression1 è true, allora il valore dell espressione condizionale è quello di expression2. Altrimenti, il valore dell espressione è quello di expression3 Esempi : voto > 18? "esame superato" : "ritenta" voto = 30; true "esame superato" voto > 18? "esame superato" : "ritenta" voto = 15; false "ritenta" precedenza Bitwise operators Associativity Operator Function Use Left bitwise NOT expr Left << left shift expr << expr Left >> right shift expr >> expr Left & bitwise AND expr & expr Left bitwise XOR expr expr Left bitwise OR expr expr lavorano su tipi interi, signed e unsigned, visi come sequenze di bit se uno dei due operandi è un intero di piccole dimensioni (char, short, ecc), viene prima convertito a int Gli operatori di shift <<) e >> Sono di due tipi: right-shift: (>>) e left-shift: (<<) spostano a sinistra (<<) o destra (>>) l operando a sinistra di un numero di bit indicato dall operando di destra unsigned char bits = 0x9B; 10011011 bits << 8 00000000 00000000 10011011 00000000 bits >> 3 00000000 00000000 00000000 00010011 L operatore left-shift: << inserisce bit con valore 0 a sinistra L operatore right-shift: >> inserisce bit concordi al segno (0 se numero positivo, 1 se negativo) a destra
Gli operatori NOT, AND, OR e XOR L operatore NOT ( ) complementa i singoli bit del tipo intero unsigned char bits = 0x9B; 10011011 ~ bits 11111111 11111111 11111111 01100100 gli altri operatori generano un nuovo intero come risulstato dell operazione logica bit a bit unsigned char b1 = 0145; 01100101 unsigned char b2 = 0257; 10101111 b1 & b2 0.. 24 bit a zero...0 00100101 b1 b2 0.. 24 bit a zero...0 11101111 L operatore sizeof L operatore sizeof ritorna la dimensione, in bytes, di un espressione oppure di un tipo il risultato è un espressione costante di tipo size_t esistono due forme : sizeof(type) sizeof expr Esempi Sales_data data, * p; sizeof(sales_data); sizeof data; sizeof p; sizeof *p; // size required for Sales_data object // same as sizeof(sales_data) // size of a pointer // size of the type at which p points // i.e. sizeof(sales_data) b1 ^ b2 0.. 24 bit a zero...0 11001010 sizeof data.isbn sizeof Sales_data::isbn // size of isbn Sales_data s member // alternative was of isbn size Le conversioni tra tipi Conversioni in espressioni aritmetiche Tutti gli operazioni vengono effettuate tra operandi dello stesso tipo se i tipi sono diversi, viene attuata una conversione int i = 3.541 + 3; i due operandi della somma sono diversi: double + int int viene promosso a double la somma viene effettuata in virgola mobile ed il risultato viene trasformato in int ed usato per inizializzare la variabile i le conversioni prendono il nome di conversioni implicite Quando avvengono? tipi interi più piccoli di int sono convertiti all intero più grande in espressioni relazionali, non-bool sono convertiti a bool in inizializzazioni, l inizializzatore è convertito al tipo della variabile in espressioni aritmetiche e relazionali con tipi misti, i tipi sono convertiti ad un tipo comune Promozioni tra interi 1. char, short (signed and unsigned) sono promossi a int 2. se dopo la prima conversione l espressione è di tipo misto: l operando di tipo inferiore è promosso al tipo superiore secondo le regole: 3. nel caso di operandi signed e unsigned, il tipo con meno bit viene convertito a quello con più bit 4. se la dimensione è la stessa, il tipo signed è convertito a quello unsigned Regole di conversione: 1. long long double l; 2. long double 3. double d; 4. float f; 5. unsigned long 6. unsigned u; 7. long l; 8. int i; 9. char c, short s; promozione
Esempi di conversioni aritmetiche bool Bflag; char Cval; short Sval; unsigned short USval; int Ival; unsigned int UIval; long Lval; unsigned long ULval; float Fval; double Dval; 3.1419 + a ; // a -> int, then int -> double Dval + Ival; Dval + Fval; Ival = Dval; Bflag = Dval; Cval + Fval; Sval + Cval; // Ival, int -> double // Fval, float -> double // Dval, double -> int (truncation) // if Dval is 0 -> false, else true // Cval, char -> int, then int -> float // Sval, short -> int, Cval, char -> int Cval + Lval; // Cval, char -> long Ival + ULval; // Ival, int -> unsigned long USval + Ival; // dipende dalle dimensioni di unsigned short e int UIval + Lval; // dipende dalle dimensioni di unsigned int e long