definir ALGO (1 << 0)

Vine a través de esta línea de código:

#define CPARSER_FLAGS_DEBUG (1 << 0) 

¿Qué hace? Es lo mismo que:

 #define CPARSER_FLAGS_DEBUG (1) 

¿Derecha?

En los lenguajes inspirados en C, los operadores << y >> son operadores de cambio a la derecha y a la derecha (aunque en C ++ pueden sobrecargarse, la sobrecarga más famosa es probablemente la de los operadores de flujo de E / S ). Por ejemplo,

 x = y << 2; 

asigna x el resultado de desplazar y a la izquierda en dos bits.

Por lo general, verá una gran cantidad de cambios de bytes en el código de bajo nivel y aquí se explica por qué ... Cualquier hardware proporciona una forma de configurar y controlar su comportamiento, pero nadie quiere usar todo el número entero para representar, digamos, el estado ON / OFF. Por lo tanto, los desarrolladores de hardware generalmente proporcionan un solo número entero (también conocido como un registro, con mayor frecuencia sin signo de 32 bits) y afirman que, por ejemplo, el bit # 0 habilita o inhabilita la transmisión de datos, el bit # 1 habilita o inhabilita el filtrado de datos, el bit # 3 sí lo hace Alguna otra magia y así una y así la fuerza. Generalmente, una o más configuraciones se pueden leer o cambiar al mismo tiempo. Ahora imagine lo conveniente que es para los desarrolladores de software: en lugar de trabajar con enteros simples (o booleanos), los progtwigdores tienen que lidiar con bits que generalmente no son direccionables por la CPU. Para simplificar sus vidas, los desarrolladores definen máscaras. Siguiendo con el ejemplo anterior, las máscaras de configuración podrían verse así:

 #define MYDEV_ENABLE_DATA_FLOW (1u<<0) #define MYDEV_ENABLE_FILTERING (1u<<1) #define MYDEV_ENABLE_MAGIC (1u<<2) 

Como se conoce el lado derecho de la expresión de cambio, el comstackdor generará los siguientes enteros para cada valor, respectivamente:

  • 1
  • 2
  • 4

Lo que al principio puede no tener mucho sentido, pero si nos fijamos en esos valores en representación binaria, el aspecto es el siguiente:

  • 0b001
  • 0b010
  • 0b100

En otras palabras, cada valor tiene solo un bit establecido en diferentes posiciones. Luego, digamos que queremos habilitar la transmisión de datos y una funcionalidad mágica, pero no habilitar el filtrado para nuestro dispositivo imaginario. Para eso, tendríamos que tener solo los bits # 0 y # 2 establecidos ( 1 ), y el bit # 1 sin establecer ( 0 ). Esto es cuando el operador OR de Bitwise junto con nuestras máscaras predefinidas son útiles. Lo que hacemos es esto:

 uint32_t value_for_device = MYDEV_ENABLE_DATA_FLOW | MYDEV_ENABLE_MAGIC; 

Que "O" s 0b001 y 0b100 , dando valor 0b101 . Enviamos esto a un dispositivo, comprueba cada bit y habilita o deshabilita la funcionalidad correspondiente.

Otras operaciones de bits se utilizan a menudo también. Por ejemplo, no sabemos qué está habilitado o deshabilitado actualmente, y no queremos cambiar nada, solo nos aseguramos de que el filtrado de datos esté desactivado. Esto se puede hacer leyendo la configuración actual, desactivando el bit y volviendo a escribirla. Por ejemplo:

 uint32_t value; value = read_from_device(); value &= (~MYDEV_ENABLE_FILTERING); write_to_device(value); 

Esto, por supuesto, no es el único uso. Para obtener una gran cantidad de ejemplos útiles, vea "Hits de Twiddling de bits" por Sean Eron Anderson .

Bien, volviendo a su pregunta original: ¿por qué escribir (1<<0) y no simplemente (1) ? Hay un número de razones:

Consistencia

Es más consistente tener algo como esto:

 #define A (1<<0) #define B (1<<1) #define C (1<<2) 

En vez de esto:

 #define A (1) #define B (1<<1) #define C (1<<2) 

O incluso esto:

 #define A 1 #define B 2 #define C 4 

Mantenibilidad

Fácil de cambiar las cosas

Hace que sea más fácil cambiar las cosas. Es más fácil cambiar las cosas cambiando solo el ancho de turno y no agregando / eliminando '<

Expresión de intención

Explica claramente la intención del autor a quienes leen el código. Sólo 1 no significa mucho. Pero cuando ve 1<<0 lo más probable es que asum que el desplazamiento de bits está involucrado y que el código funciona con máscaras de bits.

Flexibilidad

Imagine que el número por el cual se debe realizar el cambio se define como una macro. Por ejemplo:

 #define MY_BIT (1u< 

Entonces, realmente no sabes si el resultado es 1 o no. Y puede mantener las definiciones de desplazamiento de bits por separado.

Probablemente hay algunas razones más para hacer esto que no vienen a mi mente de inmediato.

Espero que se aclare un poco las cosas. ¡Buena suerte!

Sí lo es. Tal vez se usa para la simetría cuando se establecen valores para banderas:

 #define FLAG_1 (1 << 0) #define FLAG_2 (1 << 2) #define FLAG_3 (1 << 3) /* ... */ 

No se preocupe por las actuaciones, un buen comstackdor podrá optimizar tales operaciones.

Puedes combinar estos valores de la siguiente manera:

 /* Flags FLAG_1, FLAG_2 and FLAG_3 are set. */ f = FLAG_1 | FLAG_2 | FLAG_3; 

Y probar si una bandera dada se establece:

 /* True if FLAG_1 is set. */ if (f & FLAG_1) { /* ... */ } 

Esto generalmente se hace para mostrar que la definición representa una marca de bit. Esp. cuando hay múltiples banderas definidas juntas. En este caso, la magnitud del desplazamiento define la posición del bit que representa la definición. Definirlo así también hace que las cosas se alineen bien:

 #define FOO_FLAG (1 << 0) #define BAR_FLAG (1 << 1) #define BAZ_FLAG (1 << 2) 

Esto se puede usar cuando se llama a una función que espera una serie de indicadores como:

 int ret = some_function(FOO_FLAG | BAR_FLAG) & BAZ_FLAG; 

Esto llama a la función con los bits 0 y 1 (FOO & BAR) y verifica el retorno para ver si se configuró el bit 2 (BAZ).