c arrays gcc attributes struct

¿Efectos de__attribute__((packed)) en una matriz anidada de estructuras?



arrays gcc (1)

Tenga en cuenta los siguientes puntos sobre __attribute__((packed)) :

  • Cuando se usa packed en una declaración de estructura, comprimirá sus campos de tal manera que, sizeof (structure) == sizeof (first_member) + ... + sizeof (last_member).

  • Aquí, una matriz es solo un miembro de la estructura. Embalar la estructura que contiene una matriz no cambiará el tamaño de la matriz. De hecho, el tamaño de (cualquier) matriz es siempre sizeof (element) * number_of_elements.

  • De manera similar, empaquetar una estructura que contiene una estructura interna no cambiará el tamaño de la estructura interna. El tamaño de una estructura está completamente determinado por su declaración , y es el mismo sin importar dónde lo use.

  • El empaque de una estructura hará su alineación requerida un byte (es decir, puede colocarse en cualquier lugar de la memoria).

  • El embalaje presentará problemas de alineación al acceder a los campos de una estructura empaquetada. El compilador tendrá en cuenta eso cuando se accede a los campos directamente, pero no cuando se accede a ellos a través de punteros . Por supuesto, esto no se aplica a los campos con la alineación requerida uno (como char u otras estructuras empaquetadas). Vea mi respuesta a una pregunta similar , que incluye un programa que demuestra el problema de acceder a los miembros a través de punteros.

Finalmente, para responder la pregunta,

¿Hay alguna forma de garantizar que la estructura data_s no tendrá absolutamente ningún espacio adicional agregado a ella ni a ninguna de sus subestructuras, por lo que no tengo cambios dependientes del compilador en el mapa de memoria?

Sí. Declare la estructura como empaquetada, y también todas las estructuras que contiene, recursivamente.

También tenga en cuenta que el atributo empaquetado se aplica a una declaración de estructura y no a un tipo. No existe la versión empaquetada de una estructura declarada no empaquetada. Cuando utilice una estructura en alguna parte, (sus miembros) se empaquetará si y solo si la estructura misma se declaró empaquetada. Esto es algo que está implícito en el hecho de que el tamaño de una estructura está completamente determinado por su declaración.

ACTUALIZACIÓN : Por alguna razón, todavía estás confundido sobre las matrices. La solución que proporcioné (declarar todas las estructuras empaquetadas) también funciona con arreglos . Por ejemplo:

struct elem_struct { uint32_t x; } __attribute__((packed)); // packed guarantees that sizeof(struct elem_struct) = sizeof(uint32_t) = 4 struct array_struct { struct elem_struct arr[10]; } __attribute__((packed)); // packed guarantees that sizeof(struct array_struct) = // = sizeof(struct elem_struct[10]) = 10 * sizeof(struct elem_struct) // = 10 * 4 = 40

Los dos puntos adicionales que hizo sobre las matrices son ciertos, pero solo cuando las estructuras no están empaquetadas. El empaquetamiento fuerza a los campos de la estructura a ser continuos, y esto crea problemas de alineación que, si no se usaba empaquetamiento, se resolverían insertando espacio vacío entre los miembros y rellenando la estructura (vea el punto que ya planteé sobre la alineación).

El problema

Estoy trabajando en el envío de una estructura en bruto a través de una red a un programa conocido en el otro lado, pero tengo que preocuparme por la memoria introducida silenciosamente utilizada para alinear las estructuras (otros problemas como endianness están cubiertos). Con lo que estoy trabajando es algo así como:

typedef struct __attribute__((packed)) { uint16_t field1; uint16_t field2; uint16_t field3; } packed_1_s; typedef struct __attribute__((packed)) { uint16_t fieldA; uint16_t fieldB; packed_1_s structArray[10]; } packed_2_s; typedef struct __attribute__((packed)) { uint16_t fieldX; packed_2_s fieldY; uint8_t arrayZ[20]; } data_s;

Entiendo que normalmente la estructura del paquete_1_s podría / tendría espacio adicional asignado para cada instancia de la estructura para completarla con el tamaño preferido del compilador (dependiendo del hardware para el que se está construyendo), y ese tamaño favorecido puede ser desde 2 bytes a 64 bytes (más recientemente). Normalmente, si tuviera una única instancia de packed_1_s en packed_2_s, no habría ningún problema, pero me dieron a entender que hay algunas diferencias cuando intentas poner elementos en una matriz.

Intento de soluciones

La documentación de gcc parece sugerir que simplemente incluyendo el atributo empaquetado en la definición de packed_2_s, los campos, incluso si son matrices, estarán lo más empacados posible y no agregarán espacio a la estructura de packed_2_s para alinear los elementos de la matriz. La documentación sobre el atributo align () sugiere que las matrices se manejan de forma diferente que otros campos y necesita el conjunto de atributos alineados / empaquetados directamente para que modifique el espaciado adicional agregado para que coincida con la alineación especificada (o la falta de ella). Intenté configurar el atributo packed en el campo structArray, y cuando eso no funcionó, realicé una prueba estableciendo el atributo packed en arrayZ en el código anterior:

packed_1_s structArray[10] __attribute__((packed)); uint8_t arrayZ[20] __attribute__((packed));

Ambos intentos me dieron una advertencia del compilador de que el atributo empaquetado no se entendía en este contexto y se saltaría (lo bueno es que compilo con "-Wall").

Esperaba que una forma de evitar el problema fuera usar el atributo Alinear (1), que indica una alineación deseada de 1 byte que es comparable al atributo empaquetado, pero la documentación dice que el atributo align () solo puede aumentar la alineación y debe empaquetarse ser usado para disminuir la alineación.

Consideraciones

Según lo que puedo determinar a partir de la documentación de GCC, parece que hay 3 casos principales de memoria adicional insertada.

  1. Los contenidos de la estructura pueden tener memoria adicional asignada a la propia estructura para cambiar el espaciado entre los campos.
    Efectivamente, la definición del mapa de memoria de los contenidos de la estructura dentro de la estructura puede cambiar (aunque no el orden de los elementos).
  2. Es posible que las estructuras tengan memoria adicional asignada para completar un tamaño general más eficiente. En general, esto es así para que otras variables que vienen después de una declaración de una de sus instancias no caigan dentro del mismo "bloque" que la instancia de estructura donde el sistema / compilador define un "bloque".
  3. Las matrices, ya sea dentro de una estructura o no, pueden tener memoria adicional añadida para cambiar los elementos a una alineación eficiente.

Por lo que puedo decir, el atributo empaquetado se puede usar para afectar las estructuras y bloquear la memoria adicional agregada en el caso 1 y 2 anterior, pero no parece haber una manera de manejar el caso 3 anterior en mi compilador (s) )

La pregunta

¿Hay alguna forma de garantizar que la estructura data_s no tendrá absolutamente ningún espacio adicional agregado a ella ni a ninguna de sus subestructuras, por lo que no tengo cambios dependientes del compilador en el mapa de memoria? ¿Estoy entendiendo mal los casos donde el compilador puede insertar espacio para cambiar intencionalmente el mapa de memoria?

EDITAR

Discutí algunos de los problemas con mi gurú local y parece que tengo un malentendido sobre el caso 3 anterior. Los elementos en la matriz no tienen espacio insertado entre ellos, pero el espacio adicional para garantizar que se alineen correctamente se agrega a la estructura misma. Aparentemente, esto sugiere que cosas como "sizeof (structureOnlyContaining_uint32_t)" no siempre devolverán "4" ya que se puede agregar espacio adicional para alinear el tipo de datos uint32_t en el compilador que se está utilizando. El resultado es que realmente solo hay 2 casos:

  1. Mayores desplazamientos entre campos en el mapa de memoria de la estructura.
    El espacio entre los campos se puede modificar para alinear cada campo. Esto se puede cambiar usando los atributos packed o align ().
  2. Estructura final de relleno. El tamaño de una estructura, tal como lo devuelve sizeof (), se puede modificar para que las matrices de las estructuras terminen alineadas correctamente para el sistema. Esto permite que todos los sistemas asuman que el inicio de las estructuras siempre estará alineado, lo que provocará problemas si no lo están. Esto no se ve afectado por el paquete o los atributos de alineación.

Debido al nuevo caso 2, los elementos de una matriz en una estructura no obedecen necesariamente a los atributos empaquetados o alineados () especificados en la estructura, aunque el inicio de la matriz y el campo inmediatamente posterior a la matriz sí lo hacen.

Mi pregunta es cómo lidiar con structArray en packed_2_s, ya que el tamaño de la matriz como un todo no puede garantizarse únicamente por el atributo packed. ¿Hay alguna manera de garantizar el tamaño fijo del campo structArray como un todo? Cabe señalar que no puedo aumentar demasiado el tamaño de los paquetes_1_s puesto que la estructura data_s debe mantenerse lo más pequeña posible (reemplaza los datos de audio / video en un escenario de transmisión).