ProgrammazioneIngegnere C Embedded

Come funzionano gli operatori bit a bit (&, |, ^, ~, <<, >>) nel linguaggio C? Qual è la loro specificità nel lavorare con tipi di lunghezza e segno diversi, e quali sono gli errori comuni che gli sviluppatori commettono nel loro utilizzo?

Supera i colloqui con l'assistente IA Hintsage

Risposta.

Gli operatori bit a bit gestiscono i singoli bit dei tipi numerici interi:

  • & — AND bit a bit
  • | — OR bit a bit
  • ^ — XOR bit a bit
  • ~ — NOT bit a bit
  • << — shift a sinistra
  • >> — shift a destra

Caratteristiche:

  • Gli operatori funzionano solo con tipi interi (int, unsigned int, ecc.).
  • I numeri firmati (signed) durante gli shift a destra (>>) possono generare uno spostamento aritmetico o logico — dipende dal compilatore.
  • Durante gli shift di un numero di bit che supera la dimensione della variabile, si verifica un comportamento indefinito.
  • Per un funzionamento affidabile, si scelgono spesso tipi unsigned per evitare l'estensione del segno.

Esempio:

unsigned int flags = 0; flags |= 0x1; // Imposta il bit 0 flags &= ~0x2; // Resetta il bit 1 if ((flags & 0x4) != 0) { /* ... */ } // Controlla il bit 2

Domanda insidiosa.

Qual è la differenza dell'operazione di shift a destra (>>) per i tipi signed int e unsigned int?

Risposta spesso errata: Si pensa che lo shift a destra inserisca sempre zeri a sinistra, indipendentemente dal segno.

Risposta corretta: Per il tipo unsigned int, lo shift a destra (>>) inserisce sempre zeri. Per signed int, si inserisce o il segno (unità, se il numero è negativo) o zeri — dipende dall'implementazione del compilatore (architettura e regole dello standard C).

Esempio:

signed int a = -8; unsigned int b = (unsigned int)a; printf("%d ", a >> 1); printf("%u ", b >> 1);

Nel primo caso, il risultato dipende dal compilatore; nel secondo ci sarà sempre uno shift logico con zeri.

Esempi di errori reali a causa della mancata conoscenza delle sottigliezze dell'argomento.


Storia

Nel codice per il protocollo di elaborazione, le bandiere di segnale erano memorizzate nel tipo char. Il programmatore ha applicato uno shift di 8 bit (flag << 8), che a causa del sovraccarico e delle regole di promozione dei tipi ha portato alla perdita di tutti i dati — il risultato era sempre zero.


Storia

Lettura dei dati da un protocollo di rete (big-endian). L'uso di operazioni bit a bit per unire byte non era accompagnato dalla conversione a unsigned, il che a volte portava a valori negativi inaspettati durante la lettura di un campo di una struttura.


Storia

L'uso di ~ (NOT bit a bit) per resettare bit in un valore di tipo int (ad esempio, ~0x80) veniva interpretato come 0x7F, ma in realtà risultava un numero negativo -129, il che portava a errori nei successivi calcoli e verifiche logiche.