ProgramaciónIngeniero de C embebido

¿Cómo funcionan los operadores a nivel de bits (&, |, ^, ~, <<, >>) en el lenguaje C? ¿Cuál es su especificidad al trabajar con tipos de diferentes longitudes y signos, y cuáles son los errores comunes que cometen los desarrolladores al usarlos?

Supere entrevistas con el asistente de IA Hintsage

Respuesta.

Los operadores a nivel de bits gestionan bits individuales de tipos numéricos enteros:

  • & — AND a nivel de bits
  • | — OR a nivel de bits
  • ^ — XOR a nivel de bits
  • ~ — NOT a nivel de bits
  • << — desplazamiento a la izquierda
  • >> — desplazamiento a la derecha

Características:

  • Los operadores solo funcionan con tipos enteros (int, unsigned int, etc.).
  • Los números con signo (signed) pueden generar un desplazamiento aritmético o lógico al desplazar a la derecha (>>) — depende del compilador.
  • Cuando el número de bits desplazados excede la capacidad de la variable, se produce un comportamiento indefinido.
  • Para un funcionamiento seguro, a menudo se eligen tipos unsigned para evitar la extensión del signo.

Ejemplo:

unsigned int flags = 0; flags |= 0x1; // Establecer el bit 0 flags &= ~0x2; // Restablecer el bit 1 if ((flags & 0x4) != 0) { /* ... */ } // Comprobar el bit 2

Pregunta engañosa.

¿Cuál es la diferencia en la operación de desplazamiento a la derecha (>>) para los tipos signed int y unsigned int?

Respuesta comúnmente errónea: Se cree que el desplazamiento a la derecha siempre inserta ceros a la izquierda, independientemente de la firma.

Respuesta correcta: Para el tipo unsigned int, el desplazamiento a la derecha (>>) siempre inserta ceros. Para signed int, se inserta el signo (unos, si el número es negativo) o ceros — depende de la implementación del compilador (arquitectura y reglas del estándar C).

Ejemplo:

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

En el primer caso, el resultado depende del compilador; en el segundo, siempre será un desplazamiento lógico con ceros.

Ejemplos de errores reales debido al desconocimiento de los matices del tema.


Historia

En el código de procesamiento de protocolo, las banderas de señal se guardaban en un tipo char. El programador aplicó un desplazamiento de 8 bits (flag << 8), lo que resultó en la pérdida de todos los datos debido a un desbordamiento y las reglas de promoción de tipos — el resultado siempre era cero.


Historia

Lectura de datos de un protocolo de red (big-endian). El uso de operaciones a nivel de bits para combinar bytes no fue acompañado de una conversión a unsigned, lo que a veces llevaba a valores negativos inesperados al leer un campo de una estructura.


Historia

El uso de ~ (NOT a nivel de bits) para restablecer bits en un valor de tipo int (por ejemplo, ~0x80) se interpretaba como 0x7F, pero en realidad resultaba en un número negativo -129, lo que causaba errores en cálculos posteriores y verificaciones lógicas.