Uso de la directiva del compilador #pragma pack(1) en aplicaciones integradas

Recientemente me encontré con esta directiva de preprocesador #pragma pack(1) y me preguntaba por qué se usa.

Busqué en Google el uso y descubrí que tiene otras opciones, como empujar, hacer estallar, etc. ¿Alguien ha usado esto en su aplicación integrada?

Me gustaría saber algunos ejemplos de cómo/por qué ha utilizado esta directiva y en qué tipo de procesador. ¿Cuáles son las ventajas y desventajas de usar esta directiva?

Gracias.

Solo algo a tener en cuenta: los pragmas son, por definición, no estándar y los pragmas disponibles y su uso difieren según el compilador que esté usando. Las lecciones aprendidas para esta aplicación en particular NO se transferirán necesariamente a otra situación.

Respuestas (3)

#pragma pack(1)asegura que los structelementos C se empaqueten en orden y en límites de bytes. Puede ahorrar RAM, que a menudo es valiosa en un microcontrolador.

Las estructuras empaquetadas también permiten la transmisión directamente sobre los búferes de memoria para el intercambio de datos:

void f(void *buf)
{
  struct ip_header *hdr = (struct ip_header *)buf;
  hdr->dst = 0x8000001;
}

Tenga cuidado donde usa #pragma pack. Tiene un alcance global (ya que está en el preprocesador), por lo que olvidar apagarlo afectará a todos los #includearchivos. Si solo quiere empaquetar ciertas estructuras, considere usar GCC __attribute__ ((packed)).

Sin embargo, debido a problemas de alineación, las estructuras empaquetadas pueden afectar el rendimiento. Por ejemplo:

Cuando se empaqueta en bytes:

struct
{
  uint8_t  a;
  uint32_t b;
  uint8_t  c;
  uint8_t  d:
};

Se almacenará como (sin tener en cuenta el endianismo):

a, b0, b1, b2, b3, c, d

Sin embargo, muchas arquitecturas requieren que los accesos de 32 bits estén alineados con las direcciones de 32 bits. Con la estructura empaquetada, la máquina tendrá que hacer varios accesos de bytes y luego unirlos nuevamente.

Ante lo anterior structsin el empaquetado habilitado, el compilador podría reorganizarlo como:

b0, b1, b2, b3, un, c, re

En su último ejemplo, sin el empaquetado habilitado y una alineación predeterminada de 32 bits, algunos compiladores podrían agregar bytes adicionales a la estructura, para que aparezca en la memoria como a, x, x, x, b0, b1, b2, b3 , c, x, x, x, d donde x es un byte de relleno. Otras variaciones son posibles.
Gracias por tu explicación Joby y tcrosley. ¡Finalmente lo entiendo!
Es fundamental que lo apagues poniéndolo en 0 una vez que hayas terminado. No hacerlo resultará en un comportamiento extraño.
Los compiladores no pueden reorganizar el orden de los campos en las estructuras C. Puede reorganizar para poner b primero usted mismo.

Otra razón para empaquetar estructuras en límites de bytes es garantizar la alineación de los miembros al transferir datos entre diferentes procesadores.

A menudo necesito transferir estructuras de datos entre una MCU y una aplicación de PC host. La PC empaquetará estructuras en límites de 32 bits a menos que se le indique que las empaquete en límites de 1 byte. Una MCU PIC24F empaquetará estructuras en límites de 16 bits a menos que se le indique que las empaquete en límites de 1 byte.

Al indicarles a ambos que empaqueten sus estructuras en límites de 1 byte, garantiza que los datos estén en el mismo lugar cuando se accede a ellos en cualquier extremo. Sin él, necesitaría rellenar las estructuras con bytes de reserva para que los miembros de datos se alinearan correctamente.

El uso tradicional en el que he visto que se usa es para leer información de archivos. Por ejemplo, puede definir una estructura cuyos miembros coincidan con los de un encabezado de archivo BMP y luego leer todo el encabezado en una operación de lectura rápida. Bien, entonces BMP podría no ser el mejor ejemplo (su encabezado no tiene problemas de alineación en los sistemas de 32 bits), pero entiende la idea. Supongo que esto es igual de útil en el mundo integrado.

En efecto. Puede superponer una estructura en un paquete entrante (o usarla para construir uno saliente), en un conjunto de registros asignados a la memoria o construir un paquete de registro, todos los casos en los que el empaque podría diferir potencialmente de la alineación óptima de la CPU para los accesos a la memoria. .
Muy bien, pero cuidado con el endianismo. Cuando las entidades de varios bytes se asignan a una estructura, es posible que necesiten un intercambio de endian. Por ejemplo, los paquetes IP se empaquetan en big endian, pero x86 y (por lo general) ARM son little endian.