¿Por qué C no tiene literales binarios?

8 minutos de lectura

avatar de usuario
Dibujó

Con frecuencia deseo poder hacer algo como esto en c:

val1 &= 0b00001111; //clear high nibble
val2 |= 0b01000000; //set bit 7
val3 &= ~0b00010000; //clear bit 5

Tener esta sintaxis parece una adición increíblemente útil a C sin inconvenientes que se me ocurran, y parece algo natural para un lenguaje de bajo nivel donde el juego de bits es bastante común.

Editar: estoy viendo algunas otras excelentes alternativas, pero todas se desmoronan cuando hay una máscara más compleja. Por ejemplo, si reg es un registro que controla los pines de E/S en un microcontrolador, y quiero configurar los pines 2, 3 y 7 altos al mismo tiempo que podría escribir reg = 0x46; pero tuve que pasar 10 segundos pensando en ello (y probablemente tendré que pasar 10 segundos nuevamente cada vez que lea ese código después de no mirarlo por un día o dos) o podría escribir reg = (1 << 1) | (1 << 2) | (1 << 6); pero personalmente creo que es mucho menos claro que simplemente escribir `reg = 0b01000110;’ Sin embargo, puedo estar de acuerdo en que no se escala mucho más allá de las arquitecturas de 8 bits o quizás de 16 bits. No es que alguna vez haya necesitado hacer una máscara de 32 bits.

  • tiene maleficio, que en mi opinión es incluso mejor, si dedicas 10 minutos a sentir la relación

    – vroomfondel

    15 de agosto de 2013 a las 1:00

  • C tiene literales “binarios”, pero solo 2 de ellos: 0, 1. 😉

    – chux – Reincorporar a Monica

    15 de agosto de 2013 a las 4:25


  • Por lo que vale, C ++ 14 tendrá estos.

    – Nemo

    15 de agosto de 2013 a las 5:19


  • #define B00000000 0 #define B00000001 1 #define B00000010 2 #define B00000011 3#define B10100100 0xA4 … posiblemente con un yeso para unsigned char.

    – pmg

    25 de julio de 2017 a las 18:52


  • @AnT: más precisamente, 0 es un constante octal

    – chqrlie

    25 de agosto de 2018 a las 8:24

avatar de usuario
yuhao

De acuerdo a Justificación de la Norma Internacional – Lenguajes de Programación C §6.4.4.1 constantes enteras

Se rechazó una propuesta para agregar constantes binarias debido a la falta de precedentes y la utilidad insuficiente.

No está en C estándar, pero GCC lo admite como una extensión, con el prefijo 0b o 0B:

 i = 0b101010;

Ver aquí para el detalle

  • Eso es increíble, pero no estoy usando GCC 🙁 ¿sabes qué no está en el estándar?

    – Dibujó

    15 de agosto de 2013 a las 1:08

  • @Drew Ver la actualización. En otras palabras, creo que el comité cree que su uso puede cubrirse con constantes hexadecimales.

    – YuHao

    15 de agosto de 2013 a las 1:24


  • A veces pienso que las personas que escriben estándares en realidad no codifican nada en el lenguaje que están estandarizando. Eso, o todos son parte del mismo espacio tecnológico de nicho y no pueden comprender que alguien use el lenguaje para otra cosa que no sea para lo que lo usan.

    – Kurt E. Clothier

    22 de febrero a las 2:33

avatar de usuario
jacob pollock

Esto es lo que empujó hexadecimal ser… hexadecimal. Los “… El uso principal de la notación hexadecimal es una representación humana de valores codificados en binario en computación y electrónica digital…“. Sería de la siguiente manera:

val1 |= 0xF;
val2 &= 0x40;
val3 |= ~0x10;

hexadecimal:

  1. Un dígito hexadecimal puede representar un nibble (4 bits o medio octal).
  2. Dos dígitos hexadecimales pueden representar un byte (8 bits).
  3. Hex es mucho más compacto cuando se escala a máscaras más grandes.

Con algo de práctica, la conversión entre hexadecimal y binario será mucho más natural. Trate de escribir sus conversiones a mano y no use un convertidor de notación hexadecimal/bin en línea; luego, en un par de días, se volverá natural (y más rápido como resultado).

Aparte: Aunque los literales binarios no son un estándar de C, si compila con GCC es posible usar literales binarios, deben tener el prefijo ‘0b’ o ‘0B’. Ver la documentación oficial aquí para mayor información. Ejemplo:

int b1 = 0b1001; // => 9
int b2 = 0B1001; // => 9

  • Sí, eso es lo que siempre termino haciendo, pero siempre tengo que hacer un montón de cálculos en mi para recordar qué binario es qué en hax. Especialmente si quiero, por ejemplo. borrar los 6 bits más bajos. Y estoy de acuerdo en que los literales binarios se alargarían para las plataformas de 32 bits, pero en ese caso simplemente no puedes usarlos.

    – Dibujó

    15 de agosto de 2013 a las 1:06

  • Pensar en maleficio se convierte en una segunda naturaleza después de un poco de práctica. Hex también tiene la ventaja de que es más fácil de leer que binario.

    – markgz

    15 de agosto de 2013 a las 1:08

  • @Drew, entiendo tu punto de vista, ya que puede ser visualmente mucho más fácil pensar en máscaras más pequeñas. Una vez que hayas practicado lo suficiente, te resultará bastante natural (como todo sucede en la vida). Recomiendo hacer todos los cálculos a mano y verificar dos veces en una calculadora al crear máscaras para que pueda mejorar y convertir entre las dos notaciones.

    –Jacob Pollack

    15 de agosto de 2013 a las 1:09

  • ¿Cuánto cálculo mental se necesita realmente para imaginar el valor binario representado por un número hexadecimal? De todas las operaciones/cálculos que un buen programador necesita hacer en su cabeza, creo que esta es una de las más sencillas.

    – acechador

    15 de agosto de 2013 a las 1:40

  • cambie “4 bytes” a “4 bits” en el punto 1. Un nibble es 4 bits, no bytes.

    – Hans Dampf

    06/10/2015 a las 11:40

avatar de usuario
Nemo

Todos sus ejemplos se pueden escribir aún más claramente:

val1 &= (1 << 4) - 1; //clear high nibble
val2 |= (1 << 6); //set bit 6
val3 &=~(1 << 3); //clear bit 3

(Me he tomado la libertad de arreglar los comentarios para que cuenten desde cero, como pretendía Nature).

Su compilador plegará estas constantes, por lo que no hay penalización de rendimiento al escribirlas de esta manera. Y estos son más fáciles de leer que los 0b... versiones.

  • @Jerry Bueno, eso me enseñará a no dejar de pensar después del primer error. Gracias

    – Nemo

    15 de agosto de 2013 a las 1:59

  • Si tenemos en cuenta la endianidad, es (1 << 4) - 1 realmente lo mismo que 0xF ? … tal vez no.

    – Abraham Sánchez

    1 de septiembre de 2015 a las 1:54

  • @AbrahamSanchez: Sí, es exactamente igual en todas las plataformas. Las operaciones aritméticas como el desplazamiento a la izquierda se definen independientemente del endian. (De hecho, el endianness ni siquiera es detectable a menos que emita un puntero o use una unión).

    – Nemo

    1 de septiembre de 2015 a las 3:47


  • @Nemo o una matriz, una estructura o cualquier otra variable. Mientras uses algún truco como el memcpy, todo es posible.

    – yyny

    2 de marzo de 2016 a las 23:43

  • @YoYoYonnY: Todo eso requiere que “lances un puntero o uses una unión”, como dije.

    – Nemo

    02/03/2016 a las 23:51

Creo que la legibilidad es una preocupación principal. Aunque de bajo nivel, son los seres humanos quienes leen y mantienen su código, no la máquina.

¿Es fácil para ti darte cuenta de que escribiste por error? 0b1000000000000000000000000000000(0x40000000)donde realmente quieres decir 0b10000000000000000000000000000000(0x80000000) ?

“Por ejemplo, si reg es un registro que controla los pines de E/S en un microcontrolador”

No puedo evitar pensar que este es un mal ejemplo. Los bits en los registros de control tienen funciones específicas (al igual que cualquier dispositivo conectado a bits de E/S individuales).

Sería mucho más sensato proporcionar constantes simbólicas para patrones de bits en un archivo de encabezado, en lugar de calcular el binario dentro del código. Convertir binario a hexadecimal u octal es trivial, recordar lo que sucede cuando escribe 01000110 en un registro IO no lo es, especialmente si no tiene a mano la hoja de datos o el diagrama del circuito.

¡Entonces no solo ahorrará esos 10 segundos tratando de descifrar el código binario, sino tal vez un poco más de tiempo tratando de descifrar lo que hace!

avatar de usuario
philip1611

Recomiendo macros C en C para evitar advertencias del compilador u otros problemas. En lugar de 0x, uso Ox (como en “Ohio”).

#define Ob00000001 1
#define Ob10000000 (1 << (8-1))
#define Ob00001111 15
#define Ob11110000_8 (Ob00001111 << (8 - 4))
#define Ob11110000_16 (Ob00001111 << (16 - 4))
#define Ob11110000_32 (((uint32_t) Ob00001111) << (32 - 4))
#define Ob11110000_64 (((uint64_t) Ob00001111) << (64 - 4))
#define Ox0F Ob00001111
#define OxF0 Ob11110000_8
#define OxF000 Ob11110000_16
#define OxF0000000 Ob11110000_32
#define OxF000000000000000 Ob11110000_64

int main() {
    #define Ob00001110 14
    // bitwise operations work
    if (Ob00001110 == (Ob00001111 & ~Ob00000001)) {
        printf("true\n");
    }
}

Mi enfoque fue:

/* binmacro.h */

#define BX_0000 0
#define BX_0001 1
#define BX_0010 2
#define BX_0011 3
#define BX_0100 4
#define BX_0101 5
#define BX_0110 6
#define BX_0111 7
#define BX_1000 8
#define BX_1001 9
#define BX_1010 A
#define BX_1011 B
#define BX_1100 C
#define BX_1101 D
#define BX_1110 E
#define BX_1111 F

#define BIN_A(x) BX_ ## x

#define BIN_B(x,y) 0x ## x ## y
#define BIN_C(x,y) BIN_B(x,y)

#define BIN_B4(x,y,z,t) 0x ## x ## y ## z ## t
#define BIN_C4(x,y,z,t) BIN_B4(x,y,z,t)

#define BIN(x,y) BIN_C(BIN_A(x),BIN_A(y))
#define BIN4(x,y,z,t) BIN_C4(BIN_A(x),BIN_A(y),BIN_A(z),BIN_A

/*---- test ... ---*/

BIN(1101,0100)

BIN4(1101,0010,1100,0101)

¿Qué preprocesos para…

$  cpp binmacro.h
0xD4

0xD2C5

¿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