Lezione 4 – Operatori Matematici



By admin on febbraio 7, 2010


In questa lezione conosceremo gli operatori del C/C++. Gli operatori sono delle vere e proprie funzioni che prendono dei valori in ingresso e restituiscono altri valori. Gli operatori sono divisi in 2 categorie: aritmetici e logici.

 

Operatori Aritmetici

Il C/C++ forniscono gli operatori matematici fondamentali: addizione (+), sottrazione (-), moltiplicazione (*), divisione (/) e modulo (%). Vediamo subito un esempio:

void main()
{
  int a = 10, b = 5, c = 3, r;
r = a+b;   // r contiene 15 r = a-b;   // r contiene 5 r = a*b;   // r contiene 50 r = a/b;   // r contiene 2 r = a/c;   // r contiene 3 r = a%b;   // r contiene 0 (10/5 = 2 con resto 0) r = a%c;   // r contiene 1 (10/3 = 3 con resto 1) }

Nella riga:

r = a/c;

ad r viene assegnato il risultato della divisione intera 10/3, troncato all'intero inferiore, cioè 3. I risultati delle divisioni intere vengono troncati, non arrotondati.
L'operatore di modulo restituisce il resto della divisione.

E' molto frequente avere la stessa variabile sia a sinistra che a destra dell'uguale, cioè:

r = r op x

dove op è uno degli operatori matematici, r e x sono gli operandi e il risultato viene memorizzato in r. Il C (e quindi il C++) fornisce una forma compatta di questo tipo di operazione:

r op= x

Vediamo un esempio:

int x = 0;
x += 5;     // x == 5
x -= 3;     // x == 2
x *= 10;    // x == 20
x /= 2;     // x == 10
x %= 3;     // x == 1

Oltre agli operatori binari, il C mette ovviamente a disposizione anche gli operatori unari + e -, che permettono di attribuire un segno ai numeri, secondo il senso comune. Ad esempio:

x = -y;

ha il significato ovvio di assegnare a x l'opposto di y.

Operatori di incremento e decremento

Il C/C++ mette a disposizione del programmatore una forma compatta di incremento e decremento delle variabili. Nessun programmatore "serio" scriverà mai:

x += 1;

ma piuttosto:

x++;

che significa: incrementa x di 1.
Analogamente:

x--;

significa decrementa x di 1.
Ci sono due versioni degli operatori di incremento e decremento: la versione prefissa e la versione postfissa. Nel pre-incremento l'operatore ++ appare prima della variabile mentre nel post-incremento l'operatore ++ appare dopo la variabile; analogamente per il decremento.
Nel pre-incremento e pre-decremento:

++x;
--x;

l'operazione è effettuata prima della valutazione dell'espressione risultante.
Nel post-incremento e post-decremento:

x++;
x--;

l'operazione è effettuata dopo la valutazione dell'espressione.
Per capire meglio la differenza, proviamo il seguente programma:

#include <iostream>

void main()
{
  int x = 0, y = 5;
  cout << "x: " << x << endl;
  cout << "++x: " << ++x << endl;
  cout << "x++: " << x++ << endl;
  cout << "x: " << x << endl;
  cout << "y: " << y << endl;
  cout << "--y: " << --y << endl;
  cout << "y--: " << y-- << endl;
  cout << "y: " << y << endl;
}

In esecuzione si ottiene:

x: 0
x: 1
x: 1
x: 2
y: 5
y: 4
y: 4
y: 3

Per concludere, come molti hanno fatto, ci piace interpretare il nome C++ come "un passo oltre il C".

 

Operatori ternari

Sempre nell'ottica della scrittura di codice compatto, esiste un operatore ternario che permette di scegliere tra due espressioni sulla base della valutazione di una terza espressione. La forma generale è la seguente:

espressione1 ? espressione2 : espressione3

Se l'espressione1 viene valutata vera, allora il risultato dell'intera espressione è espressione2, altrimenti è espressione3. Ad esempio:

void main()
{
  int x = 3;
  // se x è uguale a 3, assegna 4 a d, altrimenti assegnagli 5
  int d = (x==3) ? 4 : 5;
}

Operatori logici

Gli operatori logici possono essere suddivisi in due gruppi: quelli normalmente usati nei confronti tra valori e quelli utilizzati per collegare i risultati di due confronti. Ecco una breve serie di esempi relativi al primo gruppo:

(a == b) // VERA se a è UGUALE a b
(a != b) // VERA se a è diversa da b
(a < b)  // VERA se a è strettamente minore di b
(a > b)  // VERA se a è strettamente maggiore di b
(a <= b) // VERA se a è minore o uguale a b
(a >= b) // VERA se a è maggiore o uguale a b

La sintassi di questi operatori ed il loro significato appaiono scontati, ad eccezione, forse, dell'operatore di uguaglianza "==": in effetti i progettisti del C, constatato che nella codifica dei programmi i confronti per uguaglianza sono, generalmente, circa la metà degli assegnamenti, hanno deciso di distinguere i due operatori "raddoppiando" la grafia del secondo per esprimere il primo. Ne segue che 

a = b;

assegna ad a il valore di b, mentre

(a == b)

esprime una condizione che è vera se le due variabili sono uguali.
Veniamo al secondo gruppo. Gli operatori logici normalmente usati per collegare i risultati di due o più confronti sono due: si tratta del prodotto logico ("&&", o and) e della somma logica ("||", o or). 

(a < b && c == d)  // AND: vera se entrambe sono VERE
(a < b || c == d)  // OR: vera se ALMENO UNA e' VERA
!(a == b)          // NOT: vera se l'espressione è false

E' possibile scrivere condizioni piuttosto complesse, ma vanno tenute presenti le regole di precedenza e di associatività. Ad esempio, poiché tutti gli operatori del primo gruppo hanno precedenza maggiore di quelli del secondo, la

(a < b && c == d)

è equivalente alla

((a < b) && (c == d)

Nelle espressioni in cui compaiono sia "&&" che "||" va ricordato che il primo ha precedenza rispetto al secondo, perciò

(a < b || c == d && d > e)

equivale a

((a < b) || ((c == d) && (d > e)))

Se ne trae, se non altro, che in molti casi usare le parentesi, anche quando non indispensabile, è sicuramente utile, dal momento che incrementa in misura notevole la leggibilità del codice e abbatte la probabilità di commettere subdoli errori logici.

 

Precedenza degli Operatori

Consideriamo ora una serie di assegnamenti:

a = b = c = d;

Il compilatore C/C++ la esegue assegnando il valore di d a c; poi il valore di c a b; infine, il valore di b ad a. Il risultato è che il valore di d è assegnato in cascata alle altre variabili; in pratica, che l'espressione è stata valutata da destra a sinistra, cioè che l'operatore di assegnamento gode di associatività da destra a sinistra. 

In altre parole, la precedenza si riferisce all'ordine in cui il compilatore valuta gli operatori, mentre l'associatività concerne l'ordine in cui sono valutati operatori aventi la stessa precedenza (non è detto che l'ordine sia sempre da destra a sinistra).
Le parentesi tonde possono essere sempre utilizzate per definire parti di espressioni da valutare prima degli operatori che si trovano all'esterno delle parentesi. Inoltre, quando vi sono parentesi tonde annidate, vale la regola che la prima parentesi chiusa incontrata si accoppia con l'ultima aperta e che vengono sempre valutate per prime le operazioni più interne. Così, ad esempio, l'espressione

a = 5 * (a + b / (c - 2));

è valutata come segue: dapprima è calcolata la differenza tra c e 2, poi viene effettuata la divisione di b per tale differenza. Il risultato è sommato ad a ed il valore ottenuto è moltiplicato per 5. Il prodotto, infine, è assegnato ad a. In assenza delle parentesi il compilatore avrebbe agito in maniera differente, infatti: 

a = 5 * a + b / c - 2;

è valutata sommando il prodotto di a e 5 al quoziente di b diviso per c; al risultato è sottratto 2 ed il valore così ottenuto viene assegnato ad a.
Vale la pena di presentare l'insieme degli operatori C/C++, riassumendone in una tabella le regole di precedenza ed associatività; gli operatori sono elencati in ordine di precedenza decrescente. 

Operatore

Descrizione

Associatività

()

chiamata di funzione 

da sx a dx

[]

indici di array 

 

.

appartenenza a struttura 

 

->

appartenenza a struttura refernziata da puntatore 

 

!

NOT logico 

da dx a sx

~

complemento a uno 

 

-

meno unario (negazione) 

 

++

autoincremento 

 

--

autodecremento 

 

&

indirizzo di 

 

*

indirezione 

 

(tipo

cast (conversione di tipo) 

 

sizeof()

dimensione di 

 

*

moltiplicazione 

da sx a dx

/

divisione 

 

%

resto di divisione intera 

 

+

addizione 

da sx a dx

-

sottrazione 

 

<< 

scorrimento a sinistra di bit 

da sx a dx

>> 

scorrimento a destra di bit 

 

minore di 

da sx a dx

<=

minore o uguale a 

 

maggiore di 

 

>=

maggiore o uguale a 

 

==

uguale a 

da sx a dx

!=

diverso da (NOT uguale a) 

 

&

AND su bit 

da sx a dx

^

XOR su bit 

da sx a dx

|

OR su bit 

da sx a dx

&&

AND logico 

da sx a dx

||

OR logico 

da sx a dx

? :

espressione condizionale 

da dx a sx

=, etc.

operatori di assegnamento (semplice e composti) 

da dx a sx

,

virgola (separatore di espressioni

da sx a dx

Come si vede, alcuni operatori possono assumere significati diversi. Il loro modo di agire sugli operandi è quindi talvolta desumibile senza ambiguità solo conoscendo il contesto di azione, cioè le specifiche espressioni in cui sono utilizzati.





Comments are closed.