definir ALGO (1

6 minutos de lectura

Me encontré con esta línea de código:

#define CPARSER_FLAGS_DEBUG        (1 << 0)

¿Qué hace? Es lo mismo que:

#define CPARSER_FLAGS_DEBUG        (1)

¿Derecha?

  • (1 << 0) == (1). Realmente (i << 0) optimized to i o (i >> 0) optimized to i me gusta ( i | i ) optimized to i. algo llamado no una expresión.

    –Grijesh Chauhan

    26 de febrero de 2013 a las 19:55

  • Es lo mismo, como 1 + 0 es igual 1. Sin embargo, está escrito de esta manera para señalar al lector que este valor está diseñado para usarse como una máscara de bits (con lsb 1 y el resto 0) en lugar de un número entero.

    – Bogdan Alexandru

    5 de diciembre de 2014 a las 11:57

definir ALGO 1
md5

Sí lo es. Tal vez se use para simetría al establecer valores para banderas:

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

No se preocupe por el rendimiento, un buen compilador podrá optimizar tales operaciones.

Puede 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 probando si una bandera determinada está configurada:

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

  • Creo que incluso un abismal el compilador podría optimizar eso.

    –Edward Thomson

    26 de febrero de 2013 a las 20:06

  • Casi parece más esfuerzo optimizarlo aquí. De todos modos, es una directiva de preprocesador, ¿no es solo hacer la única instrucción para cambiar a 0 más eficiente que intentar optimizarla activamente?

    – Foshi

    26 de febrero de 2013 a las 22:52

En lenguajes inspirados en C, << y >> los operadores son izquierda y derecha desplazamiento bit a bit operadores (aunque en C++ se pueden sobrecargar; la sobrecarga más famosa es probablemente Operadores de flujo de E/S). Por ejemplo,

x = y << 2;

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

Por lo general, verá muchos cambios de bytes en el código de bajo nivel y esta es la razón… Cualquier hardware proporciona una forma de configurar y controlar su comportamiento, pero nadie quiere usar el número entero para representar, por ejemplo, el estado ON/OFF. Por lo tanto, los desarrolladores de hardware generalmente proporcionan un solo número entero (también conocido como registro, generalmente de 32 bits sin firmar) y afirman que, por ejemplo, el bit n.° 0 habilita o deshabilita la transmisión de datos, el bit n.° 1 habilita o deshabilita el filtrado de datos, el bit n.° 3 no alguna otra magia y tan una y tan fuerza. Generalmente, una o más configuraciones se pueden leer o cambiar al mismo tiempo. Ahora imagine lo conveniente que es eso para los desarrolladores de software: en lugar de trabajar con números enteros simples (o booleanos), los programadores 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)

Dado que se conoce el lado derecho de la expresión de desplazamiento, el compilador generará los siguientes números enteros para cada valor, respectivamente:

  • 1
  • 2
  • 4

Lo que al principio podría no tener mucho sentido, pero si observa esos valores en representación binaria, se ven así:

  • 0b001
  • 0b010
  • 0b100

En otras palabras, cada valor tiene solo un bit establecido en diferentes posiciones. Luego, supongamos 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 configurados (1), y bit #1 desactivado (0). Esto es cuando O bit a bit operador 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;

¿Qué “O” 0b001 y 0b100donación 0b101 valor. Enviamos esto a un dispositivo, verifica cada bit y habilita o deshabilita la funcionalidad correspondiente.

También se utilizan a menudo otras operaciones de bits. Digamos que no sabemos qué está habilitado o deshabilitado actualmente, y no queremos cambiar nada, solo asegurarnos de que el filtrado de datos esté desactivado. Esto se puede hacer leyendo la configuración actual, desactivando el bit y reescribiéndolo. Por ejemplo:

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

Por supuesto, este no es el único uso. Para una gran cantidad de ejemplos útiles, consulte “Bit Twiddling Hacks” de Sean Eron Anderson.

Bien, volviendo a tu 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 seguir agregando/eliminando ‘<

expresión de intención

Establece claramente la intención del autor a aquellos que leen el código. Sólo 1 no significa mucho pero cuando ves 1<<0 lo más probable es que asuma que el cambio 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<<MAGIC_BIT_OFFSET)

Entonces, realmente no sabes si el resultado es 1 O no. Y puede mantener las definiciones de compensación de bits por separado.

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

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

  • +1 buena respuesta. pero (iu<<0) == (i<<0) mientras que (-1u>>0) != (-1 >> 0)

    –Grijesh Chauhan

    26 de febrero de 2013 a las 20:04


  • @GrijeshChauhan: Correcto… si se mezcla la aritmética con signos, las cosas pueden salir mal 🙂

    usuario405725

    26 de febrero de 2013 a las 20:14

definir ALGO 1
Voluntad

Esto generalmente se hace para mostrar que la definición representa un indicador 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 muy 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 este:

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

Esto luego llama a la función con bits 0 y 1 (FOO & BAR) y revisa el retorno para ver si bit 2 (BAZ) fue establecido.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad