c++ endianness c++14

¿Cuál es la endianidad de los literales binarios en C++ 14?



endianness c++14 (8)

A los lenguajes C / C ++ no les importa el endianness de los enteros de múltiples bytes. Los compiladores de C / C ++ lo hacen. Los compiladores analizan su código fuente y generan un código de máquina para la plataforma de destino específica. El compilador, en general, almacena literales enteros de la misma manera que almacena un entero; de modo que las instrucciones de la CPU de destino admitirán directamente su lectura y escritura en la memoria.

El compilador se ocupa de las diferencias entre las plataformas de destino para que no tenga que hacerlo.

La única vez que necesita preocuparse por el endianness es cuando está compartiendo valores binarios con otros sistemas que tienen diferentes ordenamientos de bytes. Luego, leería los datos binarios, byte a byte, y ordenaría los bytes en la memoria en el orden correcto para el Sistema en el que se ejecuta su código.

He intentado buscar, pero no he podido encontrar mucho sobre literales binarios y endianness. ¿Los literales binarios son little-endian, big-endian o algo más (como igualar la plataforma de destino)?

Como ejemplo, ¿cuál es el valor decimal de 0b0111 ? ¿Es 7? Plataforma específica? ¿Algo más? Edición: elegí un valor malo de 7 ya que está representado dentro de un byte. La pregunta ha sido suficientemente respondida a pesar de este hecho.

Algunos antecedentes: Básicamente, estoy tratando de averiguar cuál es el valor de los bits menos significativos, y enmascararlo con literales binarios parece una buena manera de avanzar ... pero solo si hay alguna garantía sobre la endianidad.


Además, diré que incluso el compilador no se preocupa, por ejemplo, en la plataforma LLVM solo el servidor (técnicamente no es un compilador) se encargará de la endianess.


Es posible que desee pensar en C o C ++ o cualquier otro lenguaje como intrínsecamente endian poco (piense en cómo funcionan los operadores bitwise). Si el HW subyacente es big endian, el compilador se asegura de que los datos se almacenen en big endian (igual que para otros endianness), sin embargo, sus operaciones de bit-bit funcionan como si los datos fueran little endian. Lo que hay que recordar es que, en lo que se refiere al lenguaje, los datos están en little endian. Los problemas relacionados con el endianness surgen cuando se convierten los datos de un tipo a otro. Mientras no hagas eso eres bueno.

Me preguntaron sobre la afirmación "el lenguaje C / C ++ como intrínsecamente endian", como tal, estoy proporcionando un ejemplo que muchos saben cómo funciona, pero bueno, aquí voy.

typedef union { struct { int a:1; int reserved:31; } bits; unsigned int value; } u; u test; test.bits.a = 1; test.bits.reserved = 0; printf("After bits assignment, test.value = 0x%08X/n", test.value); test.value = 0x00000001; printf("After value assignment, test.value = 0x%08X/n", test.value);

Salida en un sistema little endian:

After bits assignment, test.value = 0x00000001 After value assignment, test.value = 0x00000001

Salida en un sistema big endian:

After bits assignment, test.value = 0x80000000 After value assignment, test.value = 0x00000001

Entonces, si no conoces el endianness del procesador , ¿de dónde sale todo bien? en el sistema little endian! Por lo tanto, digo que el lenguaje C / C ++ es intrínsecamente endian poco.


La endianness está definida por la implementación. El estándar garantiza que cada objeto tiene una representación de objeto como una matriz de char y unsigned char , con la que puede trabajar llamando a memcpy() o memcmp() . En C ++ 17, es legal reinterpret_cast un puntero o referencia a cualquier tipo de objeto (no un puntero a void , un puntero a una función, o nullptr ) a un puntero a char , unsigned char , o std::byte , que Son alias válidos para cualquier tipo de objeto.

Lo que la gente quiere decir cuando habla de "endianness" es el orden de los bytes en esa representación de objeto. Por ejemplo, si declara los caracteres unsigned char int_bytes[sizeof(int)] = {1}; e int i; luego memcpy( &i, int_bytes, sizeof(i)); ¿Obtiene 0x01, 0x01000000, 0x0100, 0x0100000000000000, o algo más? La respuesta es sí. Existen implementaciones en el mundo real que producen cada uno de estos resultados, y todas se ajustan a la norma. La razón de esto es para que el compilador pueda usar el formato nativo de la CPU.

Esto ocurre con mayor frecuencia cuando un programa necesita enviar o recibir datos a través de Internet, donde todos los estándares definen que los datos deben transmitirse en orden big-endian, en una CPU little-endian como la x86. Por lo tanto, algunas bibliotecas de red especifican si los argumentos y campos de estructuras particulares deben almacenarse en el orden de bytes de red o host.

El lenguaje le permite dispararse a sí mismo al girar los bits de la representación de un objeto de forma arbitraria, pero puede obtener una representación de captura , lo que podría causar un comportamiento indefinido si intenta usarlo más adelante. (Esto podría significar, por ejemplo, volver a escribir una tabla de función virtual para inyectar código arbitrario). El <type_traits> tiene varias plantillas para probar si es seguro hacer cosas con una representación de objeto. Puede copiar un objeto sobre otro del mismo tipo con memcpy( &dest, &src, sizeof(dest) ) si ese tipo es is_trivially_copyable . Puede hacer una copia en la memoria no inicializada correctamente alineada si is_trivially_move_constructible . Puede probar si dos objetos del mismo tipo son idénticos a memcmp( &a, &b, sizeof(a) ) y hash correctamente un objeto aplicando una función de hash a los bytes en su representación de objeto si el tipo has_unique_object_representations . Un tipo integral no tiene representaciones de trampas, y así sucesivamente. Sin embargo, en la mayoría de los casos, si está realizando operaciones en representaciones de objetos donde el endianness importa, le está diciendo al compilador que asuma que sabe lo que está haciendo y que su código no será portátil.

Como han mencionado otros, los literales binarios se escriben con el dígito más significativo primero, como los literales decimales, octales o hexadecimales. Esto es diferente de endianness y no afectará si necesita llamar a ntohs() en el número de puerto desde un encabezado TCP leído desde Internet.


Respuesta corta: no hay uno .

Respuesta larga: el endianismo nunca se expone directamente en el código a menos que realmente intentes sacarlo (como usar trucos de punteros). 0b0111 es 7, son las mismas reglas que hexadecimal, escribir

int i = 0xAA77;

no significa 0x77AA en algunas plataformas porque eso sería absurdo. ¿Dónde irían los 0s extra que faltan de todas formas con las intenciones de 32 bits? ¿Se rellenarían en la parte delantera, luego todo se volcó a 0x77AA0000 , o se agregarían después? No tengo idea de lo que alguien esperaría si ese fuera el caso.

El punto es que C ++ no hace ninguna suposición sobre el endianness de la máquina, si escribe código usando primitivos y los literales que proporciona, el comportamiento será el mismo de una máquina a otra (a menos que comience a eludir el tipo de sistema, que es posible que tenga que hacer

Para abordar su actualización: el número será la forma en que lo escriba. Los bits no se reordenarán o algo así, el bit más significativo se encuentra a la izquierda y el bit menos significativo a la derecha.

Parece haber aquí un malentendido acerca de lo que es el endianismo . Endianness se refiere a cómo se ordenan los bytes en la memoria y cómo deben interpretarse. Si te di el número "4172" y dije "si son cuatro mil ciento setenta y dos, ¿cuál es la endianidad?" No puedes dar una respuesta porque la pregunta no tiene sentido. ( Algunos argumentan que el dígito más grande a la izquierda significa big endian, pero sin memoria se aborda la cuestión de la endianidad no es responsable o relevante ). Esto es solo un número, no hay bytes que interpretar, no hay direcciones de memoria. Suponiendo una representación de enteros de 4 bytes, los bytes que le corresponden son:

low address ----> high address Big endian: 00 00 10 4c Little endian: 4c 10 00 00

así que, dado cualquiera de los dos y dicho "esta es la representación interna de la computadora de 4172", se podría determinar si es pequeño o grande.

Así que ahora considera tu literal binario 0b0111 estos 4 bits representan un nybble, y se pueden almacenar como

low ---> high Big endian: 00 00 00 07 Little endian: 07 00 00 00

Pero no tiene que preocuparse porque esto también es manejado por el hardware, el lenguaje dicta que el compilador lee de izquierda a derecha, el bit más significativo al bit menos significativo.

Endianness no se trata de bits individuales . Dado que un byte es de 8 bits, si te doy 0b00000111 y digo "¿es este endian pequeño o grande?" Otra vez no puedes decir porque solo tienes un byte. Endianness no reordena los bits en un byte, se refiere a la reordenación de bytes completos (a menos que, por supuesto, tenga bytes de un bit).

No tienes que preocuparte por lo que tu computadora usa internamente. 0b0111 solo te ahorra el tiempo de tener que escribir cosas como

unsigned int mask = 7 // only keep the lowest 3 bits

por escrito

unsigned int mask = 0b0111;

Sin necesidad de comentar explicando el significado del número.


Te estás perdiendo la distinción entre el carácter interno como está escrito en el código fuente y el carácter interno como se representa en el código objeto. La respuesta para cada uno es sorprendente: los literales de código fuente son bigendianos porque así es como los humanos los leen, en el código de objeto están escritos, sin embargo, el objetivo los lee.

Dado que un byte es, por definición, la unidad más pequeña de acceso a la memoria, no creo que sea posible atribuir un endianness a cualquier representación interna de bits en un byte, la única forma de descubrir endianness para números más grandes (ya sea intencionalmente o por sorpresa) es accediendo a ellos desde el almacenamiento por partes, y el byte es, por definición, la unidad de almacenamiento accesible más pequeña.


Una imagen es a veces más que mil palabras.


Todos los literales enteros, incluidos los binarios, se interpretan de la misma manera que normalmente leemos los números (siendo el dígito más significativo el más significativo).

El estándar C ++ garantiza la misma interpretación de los literales sin tener que preocuparse por el entorno específico en el que se encuentra. Por lo tanto, no tiene que preocuparse por la endianidad en este contexto.

Tu ejemplo de 0b0111 es siempre igual a siete.

El estándar de C ++ no usa términos de endianness en lo que respecta a los literales numéricos. Más bien, simplemente describe que los literales tienen una interpretación consistente y que la interpretación es la que usted esperaría.

Estándar de C ++ - Literales enteros - 2.14.2 - párrafo 1

Un literal entero es una secuencia de dígitos que no tiene período ni parte exponente, con comillas separadoras opcionales que se ignoran al determinar su valor. Un literal entero puede tener un prefijo que especifica su base y un sufijo que especifica su tipo. El primer dígito léxico de la secuencia de dígitos es el más significativo. Un literal entero binario (base dos) comienza con 0b o 0B y consiste en una secuencia de dígitos binarios. Un literal entero octal (base ocho) comienza con el dígito 0 y consiste en una secuencia de dígitos octales. Un literal entero decimal (base diez) comienza con un dígito distinto de 0 y consiste en una secuencia de dígitos decimales. Un literal entero hexadecimal (base dieciséis) comienza con 0x o 0X y consiste en una secuencia de dígitos hexadecimales, que incluye los dígitos decimales y las letras de la A a la F y de la A a la F con valores decimales de diez a quince. [Ejemplo: el número doce puede escribirse 12, 014, 0XC o 0b1100. Los literales 1048576, 1''048''576, 0X100000, 0x10''0000 y 0''004''000''000 tienen el mismo valor. - ejemplo final]

Wikipedia describe qué es el endianness, y utiliza nuestro sistema numérico como ejemplo para entender el big-endian .

Los términos endian y endianness se refieren a la convención utilizada para interpretar los bytes que forman una palabra de datos cuando esos bytes se almacenan en la memoria de la computadora.

Los sistemas Big-endian almacenan el byte más significativo de una palabra en la dirección más pequeña y el byte menos significativo se almacena en la dirección más grande (consulte también el bit más significativo). Los sistemas Little-endian, en contraste, almacenan el byte menos significativo en la dirección más pequeña.

Un ejemplo de endianness es pensar cómo se escribe y lee un número decimal en la notación del valor de posición. Suponiendo un sistema de escritura donde los números se escriben de izquierda a derecha, la posición más a la izquierda es análoga a la dirección más pequeña de la memoria utilizada, y la posición más a la derecha es la más grande. Por ejemplo, el número ciento veintitrés se escribe 1 2 3, con los cientos de lugares a la izquierda. Cualquiera que lea este número también sabe que el dígito más a la izquierda tiene el mayor valor de posición. Este es un ejemplo de una convención de big endian seguida en la vida diaria.

En este contexto, estamos considerando que un dígito de un entero literal es un "byte de una palabra", y que la palabra sea el literal mismo. Además, se considera que el carácter que está más a la izquierda en un literal tiene la dirección más pequeña.

Con el literal 1234 , los dígitos uno, dos, tres y cuatro son los "bytes de una palabra", y 1234 es la "palabra". Con el literal binario 0b0111 , los dígitos cero, uno, uno y uno son los "bytes de una palabra", y la palabra es 0111 .

Esta consideración nos permite entender el endianness en el contexto del lenguaje C ++ y muestra que los literales enteros son similares a "big-endian".