Espressioni Logiche in Linguaggio C
Le Espressioni Logiche, chiamate anche Espressioni Booleane sono espressioni che possono assumere un valore vero o falso.
Queste espressioni possono essere costruite usando diversi tipi di operatori: operatori relazionali, operatori di uguaglianza e operatori logici.
In questa lezione studieremo come creare e combinare espressioni logiche in C usando gli operatori, quali sono le regole di precedenza e come combinare espressioni logiche e matematiche.
- Il C rappresenta i valori booleani come interi:
1
rappresenta il vero,0
rappresenta il falso; - Le espressioni logiche restituiscono come risultato un intero;
- Qualsiasi valore diverso da
0
viene considerato vero; - Per costruire un'espressione logica si possono usare gli operatori relazionali, di uguaglianza e logici.
Valori Booleani e Espressioni Logiche
Nel linguaggio di programmazione C è possibile scrivere espressioni il cui risultato può essere un valore vero o falso, ossia valori booleani. Queste espressioni prendono il nome di Espressioni Logiche o Espressioni Booleane.
In molti linguaggi di programmazione esiste un tipo apposito per rappresentare valori booleani che è in grado di assumere esclusivamente due valori. In questo senso, il linguaggio C è differente in quanto non esiste un tipo apposito.
Infatti, il C rappresenta i valori booleani sotto forma di numeri interi. In particolare, qualsiasi valore diverso da zero viene considerato vero.
Rappresentazione dei valori booleani in linguaggio C
In linguaggio C i valori booleani sono rappresentati come interi. In particolare:
- Qualsiasi valore pari a zero viene considerato falso;
- Qualsiasi valore diverso da zero viene considerato vero, anche interi negativi.
Il fatto che il risultato di un'espressione logica sia un intero ha come conseguenza il fatto che un'espressione logica è un'espressione matematica a tutti gli effetti in C, per cui possiamo combinarle. Ad esempio, usando l'operatore maggiore, >
, che vedremo in questa lezione, possiamo scrivere una riga di codice del genere:
x = (5 > 2) * 3;
In questo esempio, il valore 5 è maggiore di 2 per cui il risultato tra parentesi è vero. Al posto delle parentesi, il compilatore C userà il valore 1
, dunque alla variabile x
verrà assegnato il valore 3.
Espressioni Logiche in linguaggio C
In linguaggio C le espressioni logiche danno come risultato un intero, per cui sono espressioni matematiche a tutti gli effetti e possono essere combinate con esse.
Per costruire un'espressione logica in C si possono usare tre tipi di operatori:
- Gli operatori relazionali;
- Gli operatori di uguaglianza;
- Gli operatori logici.
Operatori Relazionali
I primi operatori logici, con cui costruire le espressioni booleane, che studiamo sono gli operatori relazionali. Questi operatori sono i seguenti:
Operatore | Operatore matematico | Significato |
---|---|---|
< |
"Minore di" | |
> |
"Maggiore di" | |
<= |
"Minore o uguale di" | |
>= |
"Maggiore o uguale di" |
Questi operatori possono essere utilizzati per comparare fra di loro sia variabili e valori interi che variabili e valori in virgola mobile. Il risultato della comparazione, come per tutte le espressioni booleane in linguaggio C produce come risultato il valore intero 0 se l'espressione è falsa, mentre restituisce 1 in caso l'espressione sia vera.
Per questo motivo, un'espressione di questo tipo:
10 < 20;
restituisce come valore 1, mentre la seguente espressione restituisce 0:
30 > 40;
Fin quando si tratta di valori o variabili di tipo numerico, interi e numeri in virgola mobile, tali operatori possono essere usati anche nel caso misto. Ossia, si possono confrontare fra di loro numeri interi e in virgola mobile. Per cui, un'espressione del genere restituisce 1:
5.23 < 10;
mentre un'espressione del genere restituisce 0:
40 > 21.2;
Per quanto riguarda la precedenza, gli operatori relazionali hanno una precedenza inferiore a quelli aritmetici. Pertanto, l'espressione che segue:
x + 1 < y * 2;
è equivalente a scrivere:
(x + 1) < (y * 2);
In ogni caso, è sempre buona norma di programmazione utilizzare le parentesi in questi casi, anche se sono ridondanti. In questo modo il codice risulta più leggibile e si evita di commettere errori quando non ci si ricorda con esattezza l'ordine di precedenza degli operatori.
Infine, gli operatori relazionali sono associativi a sinistra. Tuttavia, nel caso degli operatori relazionali, questa proprietà è di scarso utilizzo. Per comprendere meglio, prendiamo l'esempio che segue:
x < y < z;
A prima vista, potrebbe sembrare che questa espressione booleana verifichi se la variabile y
sia compresa tra x
e z
. Ma questo è sbagliato! In realtà, l'espressione di sopra equivale a:
(x < y) < z;
Per questo motivo, il compilatore C valuterà, dapprima, l'espressione x < y
. Tuttavia, essendo quest'ultima un'espressione booleana, il suo risultato sarà 0 o 1. Per cui, se x
dovesse essere minore di y
, l'espressione si traduce in:
1 < z;
Invece, se x
dovesse essere maggiore o uguale di y
, l'espressione equivalente sarà:
0 < z;
In entrambe i casi, otterremmo un'espressione completamente diversa da quella che ci aspettiamo.
Nel linguaggio C un'espressione del genere è legale:
x < y < z
Tuttavia non ha lo stesso significato che ha in matematica. Essendo gli operatori relazionali associativi a sinistra, l'espressione equivale a:
(x < y) < z
Il risultato sarebbe un confronto tra una variabile z
e il risultato 0
o 1
del confronto tra x
e y
.
Ricapitolando:
Operatori Relazionali in linguaggio C
Gli operatori relazionali in linguaggio C permettono di mettere in relazione, ossia confrontare, due espressioni. Il risultato è un intero che vale 0 nel caso in cui il risultato è falso, 1 nel caso in cui sia vero.
-
Gli operatori relazionali del linguaggio C sono:
< > <= >=
-
Gli operatori relazionali sono associativi a sinistra:
x < y < z
equivale a:
(x < y) < z
-
Gli operatori relazionali hanno una precedenza inferiore rispetto a quelli aritmetici:
x + y < z * w
equivale a:
(x + y) < (z * w)
Operatori di Uguaglianza
Gli operatori di uguaglianza del linguaggio C sono utilizzati per verificare se due variabili o valori siano uguali o differenti fra di loro. A differenza dei corrispettivi simboli matematici, tuttavia, gli operatori di uguaglianza hanno un aspetto peculiare in C:
Operatore | Operatore matematico | Significato |
---|---|---|
== |
"Uguale a" | |
!= |
"Diverso da" |
In particolare, l'operatore di uguaglianza è composto da due simboli di uguali in fila ==
, mentre l'operatore di disuguaglianza è composto da un punto esclamativo seguito da un uguale: !=
.
A causa di queste peculiarità, molti programmatori alle prime armi in linguaggio C commettono l'errore di confondere l'operatore di uguaglianza ==
con l'operatore di assegnamento =
. Per cui, ci si ritrova con espressioni del tipo x = 2
che non verificano l'uguaglianza di x
con il valore 2, bensì assegnano il valore 2 alla variabile x
.
Attenzione a non confondere l'operatore di assegnamento con l'operatore di uguaglianza. Le due espressioni che seguono non sono equivalenti:
x == y; /* Confronta x e y */
x = y; /* Assegna ad x il valore di y */
Anche nel caso degli operatori di uguaglianza il risultato è 1 in caso positivo e 0 in caso negativo. Per cui, espressioni come le seguenti:
5 == 5;
4 != 5;
restituiscono il valore 1, mentre espressioni come le seguenti:
2 == 3;
4 != 4;
restituiscono il valore 0.
Anche gli operatori di uguaglianza sono associativi a sinistra e hanno una precedenza inferiore rispetto agli operatori aritmetici. Tuttavia, gli operatori di uguaglianza hanno una precedenza inferiore agli operatori relazionali, per cui un'espressione del genere:
x < y == z > w;
equivale alla seguente espressione:
(x < y) == (z > w);
Ricapitolando:
Operatori di Uguaglianza in linguaggio C
Gli operatori di uguaglianza in linguaggio C permettono di verificare se due espressioni siano uguali, ossia restituiscano il medesimo valore, oppure no. Il risultato è un intero che vale 0 nel caso in cui il risultato è falso, 1 nel caso in cui sia vero.
-
Gli operatori di uguaglianza del linguaggio C sono:
== !=
-
Gli operatori di uguaglianza sono associativi a sinistra:
x == y != z
equivale a:
(x == y) != z
-
Gli operatori relazionali hanno una precedenza inferiore rispetto a quelli aritmetici ed una precedenza inferiore rispetto a quelli relazionali:
x + y < z * w == x * y > z + w
equivale a:
((x + y) < (z * w)) == ((x * y) > (z + w))
Operatori logici
In linguaggio C è possibile costruire espressioni logiche più complesse a partire da espressioni più semplici. Questo è possibile attraverso l'utilizzo degli Operatori Logici:
Operatore | Nome | Significato |
---|---|---|
! |
Not | Negazione logica |
&& |
And | Congiunzione logica |
|| |
Or | Disgiunzione logica inclusiva |
L'operatore Not, !
, a differenza degli altri, è un operatore unario, ossia ha bisogno di un solo operando. In generale, posto davanti ad un'espressione logica ne inverte il valore:
!(espressione)
Il risultato dell'espressione di sopra sarà 0
se espressione
vale 1
, mentre sarà 1
in caso contrario.
Ad esempio, la seguente espressione restituirà il valore 1
:
!(5 > 10)
La seguente espressione, invece, restituirà il valore 0
:
!(2 == 2)
Per quanto riguarda l'operatore And, &&
, esso restituirà il valore 1
se e soltanto se tutti e due i suoi operandi valgono 1
. Altrimenti restituisce 0
.
Ad esempio, la seguente espressione vale 1
in quanto le due espressioni valgono 1
:
(3 > 2) && (5 < 10)
La seguente espressione, invece, è falsa in quanto il secondo operando vale 0
. Pertanto il risultato sarà 0
:
(3 > 2) && (10 < 2)
Infine, l'operatore Or, ||
, restituisce 1
se almeno uno dei suoi operandi vale 1
, altrimenti restituisce 0
. Si parla, infatti, di disgiunzione logica inclusiva in quanto il risultato sarà 1
anche nel caso in cui entrambe gli operatori valgono 1
.
Ad esempio, la seguente espressione vale 1
in quanto tutti e due gli operatori valgono 1
:
(3 > 2) || (5 < 10)
Anche la seguente espressione vale 1
in quanto il primo operando è vero:
(3 > 2) || (10 < 2)
Ricapitolando:
Operatori logici in linguaggio C
Gli Operatori logici del linguaggio C permettono di combinare espressioni logiche tra di loro e restituiscono 0
o 1
.
Gli operatori logici sono:
-
Operatore Not:
!
. Si tratta di un operatore unario (ad un operando) ed inverte il risultato dell'espressione logica a cui è applicato:!(espressione)
-
Operatore And:
&&
. Restituisce1
se entrambe gli operandi valgono1
. Restituisce0
in caso contrario:(espressione_1) && (espressione_2)
-
Operatore Or:
||
. Restituisce1
se almeno uno dei suoi operandi vale1
. Restituisce0
in caso contrario:(espressione_1) || (espressione_2)
Precedenza degli operatori logici
In linguaggio C gli operatori logici &&
e ||
hanno la precedenza più bassa nelle espressioni rispetto a tutti gli altri operatori. Ad esempio la seguente espressione:
x < y && z > w
equivale a:
(x < y) && (z > w)
Inoltre, questi due operatori sono associativi a sinistra. Per cui, la seguente espressione:
(x < y) && (z > 0) && (w < 0)
equivale a:
((x < y) && (z > 0)) && (w < 0)
L'operatore Not, invece, ha la stessa priorità del meno unitario.
Caratteristiche degli operatori And e Or
Gli operatori logici &&
e ||
hanno le seguenti proprietà:
-
Hanno la precedenza inferiore a tutti gli altri operatori:
x < y && z > w
equivale a:
(x < y) && (z > w)
-
Sono associativi a sinistra:
(x < y) && (z > 0) && (w < 0)
equivale a:
((x < y) && (z > 0)) && (w < 0)
Operatori Logici ed Espressioni Matematiche
Gli operatori logici lavorando combinando espressioni logiche, ossia espressioni il cui risultato può essere 0
e 1
. Questo, tuttavia, non è un vincolo stringente. Anzi, gli operatori logici trattano come 1
qualsiasi valore diverso da 0
.
Per cui, possiamo scrivere un'espressione del genere:
(x * 2) && (y < 5)
In tal caso, se il primo operando (x * 2)
risulta avere un valore diverso da zero, esso verrà considerato come 1
dall'operatore &&
.
In questo modo è possibile combinare espressioni matematiche ed espressioni logiche. Si potrebbe sostituire, ad esempio, l'espressione che segue:
(x != 0) && (y != 0)
con l'espressione che segue:
x && y
Infatti, l'espressione x != 0
è equivalente alla semplice espressione x
in quanto risulta vera se e soltanto se x
non vale 0
.
Si noti che anche i valori negativi vengono considerati veri. Ad esempio, la seguente espressione risulta vera:
(-1) && 1
Tuttavia, è meglio evitare di scrivere espressioni logiche in questo modo perché potrebbe rendere poco leggibile i nostri programmi.
Espressioni Matematiche ed Operatori Logici in linguaggio C
In linguaggio C qualunque valore diverso da zero viene considerato vero dagli operatori logici, quindi pari a 1
.
Lo zero viene considerato sempre falso.
Cortocircuito degli Operatori Logici
Un'altra importante caratteristica degli operatori logici in linguaggio C è che essi valutano i propri operandi in cortocircuito.
Per capire cosa significa, partiamo da un esempio:
(x != 0) && (y > 0)
Si tratta di un operatore And, &&
, tra due espressioni. Questo operatore restituirà 1
, ossia vero, se e soltanto se le due espressioni sono vere. Tuttavia, se la prima espressione fosse falsa, ossia se x
è pari a zero, il risultato non potrebbe mai essere 1
indipendentemente dal risultato della seconda espressione.
In altre parole, anche se la variabile y
fosse maggiore di zero, il risultato sarebbe sempre 0
ossia falso.
Per questo motivo, il compilatore C sfrutta questa osservazione per generare un codice che sostanzialmente non valuta il secondo operando nel caso in cui il primo operando sia negativo. Ossia, il compilatore risparmia la verifica della positività della variabile y
nel caso in cui x
sia pari a zero.
Usando una metafora elettronica, si dice che l'operatore &&
ha cortocircuitato il secondo operando.
La stessa cosa vale per l'operatore Or, ||
. In tal caso, se il primo operando vale 1
risulta inutile valutare il secondo operando.
Cortocircuito degli Operatori Logici
In linguaggio C gli operatori logici binari, &&
e ||
, valutano i propri operandi in corto circuito.
- L'operatore And,
&&
, non valuta il secondo operando se il primo è pari a0
, e restituisce0
in tal caso. - L'operatore Or,
||
, non valuta il secondo operando se il primo è pari a1
, e restituisce1
in tal caso.
Il fatto che gli operatori logici &&
e ||
siano valutino gli operandi in cortocircuito presenta dei vantaggi. Per esempio, supponiamo di voler valutare se la divisione tra due variabili x
e y
sia maggiore di zero. Tuttavia, dobbiamo fare attenzione al caso in cui y
sia uguale a zero, in quanto una divisione per zero potrebbe far arrestare il nostro programma.
Potremmo prima verificare che y
sia diversa da zero (attraverso un'istruzione condizionale if
che vedremo nelle prossime lezioni) e poi controllare che la divisione sia maggiore di zero.
Oppure, possiamo sfruttare il cortocircuito in questo modo:
(y != 0) && (x / y > 0)
In questo modo, infatti, se la variabile y
fosse uguale a zero, attraverso il cortocircuito non verebbe valutata la divisione evitando così una divisione per zero.
Cortocircuito ed Effetti Collaterali
Il fatto che gli operatori logici valutino gli operandi in cortocircuito potrebbe, però, portare a delle conseguenze dannose.
Ad esempio, prendendo la seguente espressione:
x > 0 && ++y > 0
In base alla precedenza degli operatori, il secondo operando consiste nell'incrementare la variabile y
per poi verificare se è maggiore di zero.
Tuttavia, se x
è inferiore o uguale a zero, il secondo operando non verrà valutato, per cui può accadere che la variabile y
non venga incrementata.
Per risolvere questo problema si può invertire l'ordine degli operandi (l'operatore And è commutativo):
++y > 0 && x > 0
Oppure si può incrementare la variabile y
separatamente e poi verificare la condizione.
In Sintesi
In questa lezione abbiamo introdotto le espressioni logiche in linguaggio C. Abbiamo visto che non esiste un tipo booleano in C ma che i valori di vero e falso sono rappresentati sotto forma di interi: 1
e 0
.
Le espressioni logiche restituiscono un intero e possono essere costruite usando diversi operatori. Possono essere anche combinate con espressioni matematiche.
Vedremo nelle prossime lezioni come utilizzare le espressioni logiche nelle istruzioni di selezione e nelle istruzioni di ciclo.