Una máscara es un valor binario que utilizamos para seleccionar o modificar bits específicos dentro de una variable o registro.
Podéis pensar en las máscaras como si quisiéramos dibujar en una papel, y pusiéramos una cartulina con una forma recortada para usar de plantilla y no salirnos.
Las máscaras se utilizan comúnmente en combinación con operadores de bits para realizar operaciones de manipulación de bits (como la limpieza, establecimiento o verificación de bits).
El uso de máscaras, es frecuente está relacionado con los conceptos de flags y registros. Podéis usar máscaras en otros casos, pero normalmente sea al usar registros.
Qué son los registros y flags
Un registro es un número binario que aprovechamos para “guardar varias cosas”. En un registro, cada bit (o grupo de bytes) tiene un significado específico.
Por ejemplo, imagina que haces una comunicación de datos entre dos máquinas. Aquí, podrías tener un registro cuyos bits que representen el estado de la comunicación, como:
- Bit 0: Éxito de la transmisión.
- Bit 1: Error de paridad.
- Bit 2: Datos recibidos correctamente.
- Bit 3: Paquete recibido completo.
- Y así sucesivamente…
Estos bits son conocidos como Flags, o indicadores de estado. Son bits específicos en un registro que indican ciertas condiciones o resultados después de ejecutar una instrucción o una operación.
Esta es una forma conveniente de reducir la cantidad de memoria necesaria y mejorar la velocidad de los cálculos. Pero, por contra, nos obliga a tener mecanismos para comprobar solo un grupo de los bits.
Qué son las máscaras
Una máscara es una secuencia de bits, donde cada bit actúa como selector o “interruptor” que activa o desactiva la correspondiente posición en el dato objetivo.
Las máscaras son simplemente un número binario que contiene 0
en las posiciones que nos interesan, y 1
en las posiciones que sí (o al revés, depende de la operación que queramos realizar).
Luego podemos jugar con AND y OR para ejecutar acciones sobre un número binario, usando esta máscara para actuar sólo en la parte que nos interesa.
Uso de máscaras
Obtener Bits con máscaras
El caso más sencillo es usar una máscara para leer un subconjunto de bits de un registro. Para ello, vamos a aplicar al registro nuestra máscara con una operación AND.
- Los valores que son
0
en la máscara, se eliminan - Los valores que son
1
en la máscara, tienen el mismo valor que tuvieran en la registro.
Por ejemplo, para extraer un campo de bits de una variable, podemos crear una máscara con unos en las posiciones correspondientes al campo que queremos extraer y ceros en las demás posiciones.
variable = 10110110 // Variable de 8 bits
mascara = 00001111 // Máscara para los últimos 4 bits
resultado = variable & mascara // Extraer los últimos 4 bits
Con esto, obtendríamos 00000110
, que son los últimos 4 bits del registro original.
Manipulación de Bits con máscaras
También podemos usar máscaras para modificar o consultar bits específicos en una secuencia de bits más grande. Para ello jugaremos otra vez con AND y OR, y el valor del propio registro.
Ejemplo de establecer un bit Por ejemplo, supongamos que tenemos una variable de 8 bits y queremos establecer el tercer bit en 1 mientras mantenemos los otros bits sin cambios. Podemos lograr esto utilizando una máscara y el operador OR a nivel de bits:
variable = 01101001 // Variable de 8 bits inicializada en 00001000
mascara = 01101101 // Máscara para el tercer bit
resultado = variable | mascara // Establecer el tercer bit en 1
Al hacer una operación OR del valor de la propia variable, con la máscara
- Si en la máscara hay un
0
, el valor final será el que tuviera en la variable - Si en la máscara hay un
1
, el valor final será1
Es decir, el operador OR impone sus los 1’s.
Así que con la máscara y OR hemos puesto a 1
el flag 3 del registro, dejando el resto inalterados. Con lo que el resultado sería 01101101
.
Ejemplo de eliminar un bit
Veamos el caso contrario, ahora queremos forzar un 0
en la variable. Para eso vamos a usar el operador AND a nivel de bits.
variable = 00011101 // Variable de 8 bits inicializada en 00001000
mascara = 11111011 // Máscara para el tercer bit (invertimos el bit en la posición 2)
// Limpiar el tercer bit usando la máscara.
resultado = variable & mascara;
Al hacer una operación AND del valor de la propia variable, con la máscara
- Si en la máscara hay un
1
, el valor final será el que tuviera en la variable - Si en la máscara hay un
0
, el valor final será0
Es decir, el operador AND impone sus 0’s.
Así que con la máscara y AND hemos puesto a 0
el bit 3 del registro, dejando el resto inalterados. Con lo que el resultado sería 00011001
.
Esto está bien para explicarlo, pero hemos tenido que hacer la máscara “al revés”, con 0
donde queremos actuar, y 1
donde no queremos actuar. No tiene nada malo, pero no es lo habitual.
En general, lo normal sería dejar la máscara “normal”, e invertirla antes de aplicarla
variable = 00011101 // Variable de 8 bits inicializada en 00001000
mascara = 00000100 // Máscara para el tercer bit
// Limpiar el tercer bit usando la máscara.
resultado = variable & ~mascara;