Next: Funzioni
Up: Costrutti condizionali e iterativi
Previous: Il costrutto di scelta
  Indice
  Indice analitico
Concludiamo il capitolo, come promesso, con la trattazione degli operatori bit a bit del C++. Conoscenza base per l'utilizzo di tali operatori è il sistema binario, di cui forniamo una veloce introduzione. Il sistema che utilizziamo comunemente è il sistema decimale, così detto perché ogni numero
3.6può essere rappresentato come sequenza di dieci cifre (0,1,2,3,4,5,6,7,8,9); questo sistema è, come quello binario che vedremo tra poco, posizionale: ogni cifra assume un diverso peso a seconda della posizione che esso occupa; per esempio sappiamo che
Del tutto analogo ad esso è il sistema binario, costituito però da due sole cifre: 0 e 1; così è ad esempio
quindi è
. Tale esempio fornisce anche un metodo pratico per la conversione dei numeri dalla base
alla base
. Per effettuare la conversione inversa il metodo più veloce è il metodo dei resti: si divide il numero in base
per
segnando i resti, fino a quando il quoziente non è nullo; a questo punto basta prendere i resti nell'ordine inverso rispetto a quello con il quale li abbiamo ottenuti; esempio:
37 |
: | 2 |
= | 18 |
| 1 |
18 |
: | 2 |
= | 9 |
| 0 |
9 |
: | 2 |
= | 4 |
| 1 |
4 |
: | 2 |
= | 2 |
| 0 |
2 |
: | 2 |
= | 1 |
| 0 |
1 |
: | 2 |
= | 0 |
| 1 |
per cui
. Per inciso, si sappia che il processore effettua i calcoli esclusivamente in base binaria.
In C++ esiste il tipo unsigned int che permette di memorizzare variabili con valori interi non negativi, i quali sono rappresentati nel sistema binario; sfruttando tale rappresentazione e gli operatori di confronto bit a bit, possiamo manipolare sequenze di zeri e uno, un cui utilizzo tipico è mostrato nell'esempio a fine paragrafo. I primi operatori bit a bit (bitwise operators) sono i seguenti:
& |
and bit a bit |
| |
or bit a bit |
^ |
xor bit a bit |
~ |
not bit a bit |
dei quali i primi tre sono binari mentre l'ultimo è unario; per gli operatori binari esistono anche delle scorciatoie sintattiche: &=
, |=
, ^=
, le quali permettono di effettuare un'operazione sui bit e un assegnamento con una sola espressione; per esse vale il medesimo discorso condotto sulle forme contratte degli operatori aritmetici (+=
, ...).
Gli operatori binari accettano come argomenti due unsigned int e, dopo un opportuno confronto bit a bit (cioè cifra binaria per cifra binaria: bit = Binary digIT), ne restituiscono un terzo come risultato; le tabelle di verità di tali operatori sono le seguenti:
L'operatore XOR (xor = eXclusive OR) è l'OR esclusivo: esso torna
solo nel caso i due bit a confronto siano diversi. L'operatore NOT (~
) corrisponde alla negazione logica del bit, così come gli operatori AND e OR corrispondono alla intersezione e all'unione logica; si noti la differenza sintattica tra tali operatori e gli operatori logici (AND logico = &&
, OR logico = ||
). Qualche esempio
3.7:
00010110 |
& |
10110101 |
= |
00010100 |
|
00010110 |
| |
10110111 |
= |
10110111 |
|
00010110 |
^ |
10110101 |
= |
10100011 |
|
Esistono infine altri due operatori bit a bit, detti operatori di shift (scorrimento); essi sono:
<< |
shift a sinistra |
>> |
shift a destra |
Essi sono entrambi binari: l'argomento sinistro è un unsigned int corrispondente al numero da shiftare
3.8, l'argomento destro è un int che indica di quante cifre far scorrere tale numero; esempi:
Si noti che tramite questi ultimi due operatori si perde un numero di cifre esattamente uguale alla lunghezza dello scorrimento; le posizione che restano vacanti dopo lo scorrimento, vengono riempite con zeri. Si faccia attenzione: (a << n) >>
è una sequenza binaria che, in generale, è diversa da a
; ad esempio:
10010011 << 3
= 10011000
10011000 >> 3
= 00010011
ma 10010011
è ben diverso da 00010011
. Si noti che far scorrere di
cifre un numero binario corrisponde a moltiplicarlo per
, e analogamente facendolo scorrere a destra lo si divide per
:
Se questa proprietà può stupire, si pensi solo alle regole che ci sono state insegnate alle scuole elementari per moltiplicare o dividere per 10:
in realtà non facciamo altro che scorrere a destra o sinistra il numero dato.
Un utilizzo tipico degli unsigned int è la rappresentazione di flags (bandierine), cioè di insiemi di variabili che possono assumere solo due valori (bianco e nero, acceso e spento, vero e falso, abilitato e disabilitato, ...); presentiamo un esempio che permette di accendere o spegnere una serie di 8 lampadine:
// ex3_7_1.cpp
#include <iostream.h>
void main() {
unsigned int lampadine = 0; // inizialmente le lampadine sono
// sono tutte spente
bool continua = true;
unsigned int maschera; // variabile utilizzata per
// operare sui bit
int posizione; // utilizzata nello switch
unsigned int risultato; // idem
do {
cout << "\n\nscegli l'operazione da eseguire: "
"\n\t (a) stampa la serie di lampadine"
"\n\t (b) accendi una lampadina"
"\n\t (c) spegni una lampadina"
"\n\t (d) accendi tutte le lampadine"
"\n\t (e) spegni tutte le lampadine"
"\n\t (q) esci";
cout << "\n? ";
char comando;
cin >> comando;
switch (comando) {
case 'a': case 'A':
maschera = 1; // 1 = 00000001
for (int i = 0; i < 8; i++) {
risultato = lampadine & maschera;
risultato >>= i;
if (risultato == 1) // lampadina ON
cout << "A";
else // lampadina OFF
cout << "S";
maschera <<= 1;
}
break;
case 'b': case 'B':
maschera = 1;
cout << "\n\tposizione? ";
cin >> posizione;
if ( (posizione < 1) || (posizione > 8) )
break;
maschera <<= posizione - 1;
lampadine |= maschera;
break;
case 'c': case 'C':
maschera = 1;
cout << "\n\tposizione? ";
cin >> posizione;
if ( (posizione < 1) || (posizione > 8) )
break;
maschera <<= posizione - 1;
maschera = ~maschera;
lampadine &= maschera;
break;
case 'd': case 'D':
maschera = 1;
for (int i = 0; i < 8; i++) {
lampadine |= maschera;
maschera <<= 1;
}
break;
case 'e': case 'E':
lampadine = 0; // 0 = 00000000
break;
case 'q': case 'Q':
continua = false;
break;
default:
cout << "\n\n d'oh \n";
}
} while (continua == true);
}
Se ad una prima lettura il codice sembra di facile comprensione, ciò
significa che si ha già una discreta confidenza con i manipolatori di cifre
binarie, la qual cosa non è del tutto comune per uno studente di scuola
media superiore, e tantomeno per un appassionato di informatica personale; non ci si preoccupi dunque delle difficoltà che probabilmente si incontrano su tale argomento, il quale meriterebbe una ben più ampia discussione e tempi lunghi di assimilazione; in mancanza di entrambi, si provi a modificare il precedente esempio aggiungendo semplici operazioni come: ``accendi la lampadina 3'' oppure ``accendi le lampadine spente e spegni quelle accese'' o ancora ``accenti una lampadina sì ed una no''.
Next: Funzioni
Up: Costrutti condizionali e iterativi
Previous: Il costrutto di scelta
  Indice
  Indice analitico
Claudio Cicconetti
2000-09-06