¿Por qué la endianidad de bit es un problema en bitfields?


Cualquier código portable que use bitfields parece distinguir entre plataformas little - y big-endian. Vea la declaración de struct iphdr en el kernel de linux para un ejemplo de dicho código. No entiendo por qué poco peso es un problema en absoluto.

Hasta donde yo entiendo, los bitfields son construcciones puramente compiladoras, usadas para facilitar manipulaciones a nivel de bits.

Por ejemplo, considere el siguiente campo de bits: {[0]} Aquí, la escritura d->f2 es simplemente un compacto y legible manera de decir (i>>1) & (1<<4 - 1).

Sin embargo, las operaciones de bits están bien definidas y funcionan independientemente de la arquitectura. Entonces, ¿cómo es que los bitfields no son portátiles?

Author: Leonid99, 2011-05-18

6 answers

Según el estándar C, el compilador es libre de almacenar el campo de bits prácticamente de cualquier manera aleatoria que desee. Puede nunca hacer suposiciones de dónde se asignan los bits. Aquí hay algunas cosas relacionadas con el campo de bits que no están especificadas por el estándar C:

Comportamiento no especificado

  • La alineación de la unidad de almacenamiento direccionable asignada para contener un campo de bits (6.7.2.1).

Comportamiento definido por la implementación

  • Si un campo de bits puede ir a horcajadas de un límite de unidad de almacenamiento (6.7.2.1).
  • El orden de asignación de los campos de bits dentro de una unidad (6.7.2.1).

Big/little endian también está definido por la implementación. Esto significa que su estructura podría asignarse de las siguientes maneras (asumiendo ints de 16 bits):

PADDING : 8
f1 : 1
f2 : 3
f3 : 4

or

PADDING : 8
f3 : 4
f2 : 3
f1 : 1

or

f1 : 1
f2 : 3
f3 : 4
PADDING : 8

or

f3 : 4
f2 : 3
f1 : 1
PADDING : 8

¿Cuál se aplica? Adivina, o lee en profundidad la documentación del backend de tu compilador. Agregue la complejidad de enteros de 32 bits, en endian grande o pequeño, a esto. A continuación, añadir el hecho de que el compilador puede agregar cualquier número de bytes de relleno en cualquier lugar dentro de su campo de bits, porque se trata como una estructura (no puede agregar relleno al principio de la estructura, pero en cualquier otro lugar).

Y luego ni siquiera he mencionado lo que sucede si usa "int" como campo de bits type = comportamiento definido por la implementación, o si usa cualquier otro tipo que (sin signo) int = comportamiento definido por la implementación.

Así que para responder a la pregunta, no hay tal cosa como código de campo de bits portátil, porque el estándar C es extremadamente vago con cómo se deben implementar los campos de bits. Lo único que se puede confiar en los campos de bits es que sean trozos de valores booleanos, donde el programador no se preocupa de la ubicación de los bits en la memoria.

La única solución portátil es usar los operadores de bits en lugar de los campos de bits. El código de máquina generado será exactamente el mismo, pero determinista. Los operadores bit-wise son 100% portátiles en cualquier compilador de C para cualquier sistema.

 63
Author: Lundin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-05-18 11:51:27

Hasta donde yo entiendo, los bitfields son construcciones puramente compiladoras

Y eso es parte del problema. Si el uso de los campos de bits se restringiera a lo que el compilador 'poseía', entonces cómo el compilador empacó los bits o los ordenó no sería de preocupación para nadie.

Sin embargo, los campos de bits probablemente se usan mucho más a menudo para modelar construcciones que son externas a los registros de hardware de dominio del compilador, el protocolo' wire ' para comunicaciones o el archivo diseño de formato. Estas cosas tienen requisitos estrictos de cómo los bits tienen que ser establecidos, y el uso de campos de bits para modelarlos significa que usted tiene que confiar en la implementación definida y-peor aún-el comportamiento no especificado de cómo el compilador diseñará el campo de bits.

En resumen, los campos de bits no se especifican lo suficientemente bien como para hacerlos útiles para las situaciones para las que parecen ser más utilizados.

 12
Author: Michael Burr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-05-18 14:37:46

ISO / IEC 9899: 6.7.2.1 / 10

Una implementación puede asignar cualquier unidad de almacenamiento direccionable lo suficientemente grande para mantener un poco de campo. Si hay suficiente espacio sigue siendo, un poco de campo que inmediatamente sigue otro campo de bits en un la estructura se embalará en bits adyacentes de la misma unidad. Si sigue siendo insuficiente el espacio, ya sea bit-campo que no encaja se pone en la siguiente unidad o se superpone adyacente unidades está definida por la implementación. El orden de asignación de campos de bits dentro de una unidad (de orden alto a orden bajo o de orden bajo a orden alto) es implementación-definida. Alineacion de la unidad de almacenamiento direccionable es sin especificar.

Es más seguro usar operaciones de desplazamiento de bits en lugar de hacer suposiciones sobre el orden o la alineación de campos de bits cuando se intenta escribir código portátil, independientemente de la endianidad del sistema o la bitness.

Véase también EXP11-C. No aplicar operadores que esperan un tipo a los datos de un tipo.

 8
Author: mizo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-05-13 23:16:18

Los accesos a los campos de bits se implementan en términos de operaciones sobre el tipo subyacente. En el ejemplo, unsigned int. Así que si tienes algo como:

struct x {
    unsigned int a : 4;
    unsigned int b : 8;
    unsigned int c : 4;
};

Cuando accede al campo b, el compilador accede a un unsigned int completo y luego cambia y enmascara el rango de bits apropiado. (Bueno, no tiene que, pero podemos pretender que lo hace.)

En big endian, el diseño será algo como esto (primero el bit más significativo):

AAAABBBB BBBBCCCC

En little endian, el diseño será así:

BBBBAAAA CCCCBBBB

Si quieres acceder al diseño de big endian desde little endian o viceversa, tendrás que hacer un trabajo extra. Este aumento en la portabilidad tiene una penalización en el rendimiento, y dado que el diseño de la estructura ya no es portable, los implementadores de lenguaje optaron por la versión más rápida.

Esto hace muchas suposiciones. También tenga en cuenta que sizeof(struct x) == 4 en la mayoría de las plataformas.

 5
Author: Dietrich Epp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-05-18 11:08:22

Los campos de bits se almacenarán en un orden diferente dependiendo de la endianidad de la máquina, esto puede no importar en algunos casos, pero en otros puede importar. Digamos, por ejemplo, que su estructura ParsedInt representa banderas en un paquete enviado a través de una red, una máquina little endian y una máquina big endian leen esas banderas en un orden diferente del byte transmitido, lo que obviamente es un problema.

 1
Author: Charles Keepax,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-05-18 11:00:46

Para hacer eco de los puntos más destacados: Si está utilizando esto en una sola plataforma de compilador/HW como una construcción solo de software, entonces la endianidad no será un problema. Si está utilizando código o datos en varias plataformas O necesita coincidir con diseños de bits de hardware, entonces ES un problema. Y un lote de software profesional es multiplataforma, por lo tanto, tiene que preocuparse.

Aquí está el ejemplo más simple: Tengo código que almacena números en formato binario al disco. Si no escribo y lea estos datos al disco yo mismo explícitamente byte por byte, entonces no será el mismo valor si se lee desde un sistema endian opuesto.

Ejemplo concreto:

Int16_t s = 4096; // un número firmado de 16 bits...

Digamos que mi programa se envía con algunos datos en el disco que quiero leer. Digamos que quiero cargarlo como 4096 en este caso...

Fread((void*)&s, 2, fp); // lectura del disco como binario...

Aquí lo leo como un valor de 16 bits, no como explícito byte. Esto significa que si mi sistema coincide con el peso se almacena en el disco, me da 4096, y si no, tengo 16 !!!!!

Así que el uso más común de la endianidad es cargar números binarios a granel, y luego hacer un bswap si no coincide. En el pasado, almacenábamos datos en el disco como big endian porque Intel era el hombre extraño y proporcionaba instrucciones de alta velocidad para intercambiar los bytes. Hoy en día, Intel es tan común que a menudo hace que Little Endian sea el predeterminado y se intercambie cuando está en un big endian sistema.

Un enfoque más lento, pero neutro endiano es hacer TODAS las E / S por bytes, es decir:

Uint_8 ubyte; int_8 sbyte; int16_t s; / / leer s en endian neutral way

/ / Vamos a elegir little endian como nuestro orden de bytes elegido:

Fread ((void*)&ubyte, 1, fp); / / Solo se lee 1 byte a la vez fread ((void*) & sbyte, 1, fp); // Solo lee 1 byte a la vez

// Reconstruir s

S = ubyte / (sByte

Tenga en cuenta que esto es idéntico al código en el que escribiría haga un intercambio de endian, pero ya no es necesario comprobar la endianidad. Y puede usar macros para que esto sea menos doloroso.

He utilizado el ejemplo de los datos almacenados utilizados por un programa. La otra aplicación principal mencionada es para escribir registros de hardware, donde esos registros tienen un orden absoluto. Un lugar MUY COMÚN que esto surge es con gráficos. Obtener la endianness mal y sus canales de color rojo y azul se invierten! Una vez más, el problema es uno de portabilidad-simplemente podría adaptarse a una plataforma de hardware y tarjeta gráfica dada, pero si desea que su mismo código funcione en diferentes máquinas, debe probar.

Aquí está una prueba clásica:

Typedef union { uint_16 s; uint_8 b[2];} EndianTest_t;

EndianTest_t test = 4096;

If (test.b[0] == 12) printf ("Big Endian Detected!\n");

Tenga en cuenta que los problemas de campo de bits también existen, pero son ortogonales a los problemas de endianidad.

 0
Author: user2465201,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-02 18:08:05