Me preguntaba si alguien podría explicarme qué hace la #pragma pack
declaración del preprocesador y, lo que es más importante, por qué uno querría usarla.
Revisé la página de MSDN , que ofrecía algunas ideas, pero esperaba escuchar más de personas con experiencia. Lo he visto en código antes, aunque parece que ya no puedo encontrar dónde.
c
c-preprocessor
pragma-pack
Cenoc
fuente
fuente
#pragma
directivas, están definidas en la implementación.A mod s = 0
donde A es la dirección y s es el tamaño del tipo de datos; Esto comprueba si los datos no están desalineados.Respuestas:
#pragma pack
indica al compilador que empaque los miembros de la estructura con una alineación particular. La mayoría de los compiladores, cuando declara una estructura, insertará relleno entre los miembros para asegurarse de que estén alineados con las direcciones apropiadas en la memoria (generalmente un múltiplo del tamaño del tipo). Esto evita la penalización de rendimiento (o error absoluto) en algunas arquitecturas asociadas con el acceso a variables que no están alineadas correctamente. Por ejemplo, dados enteros de 4 bytes y la siguiente estructura:El compilador podría elegir colocar la estructura en la memoria de esta manera:
y
sizeof(Test)
sería 4 × 3 = 12, aunque solo contenga 6 bytes de datos. El caso de uso más común para#pragma
(según mi conocimiento) es cuando se trabaja con dispositivos de hardware en los que debe asegurarse de que el compilador no inserte relleno en los datos y que cada miembro siga el anterior. Con#pragma pack(1)
, la estructura anterior se presentaría así:Y
sizeof(Test)
sería 1 × 6 = 6.Con
#pragma pack(2)
, la estructura anterior se presentaría así:Y
sizeof(Test)
sería 2 × 4 = 8.El orden de las variables en struct también es importante. Con variables ordenadas de la siguiente manera:
y con
#pragma pack(2)
, la estructura se presentaría así:y
sizeOf(Test)
sería 3 × 2 = 6.fuente
#pragma
se usa para enviar mensajes no portátiles (como en este compilador solamente) al compilador. Cosas como deshabilitar ciertas advertencias y estructuras de embalaje son razones comunes. Deshabilitar advertencias específicas es particularmente útil si compila con las advertencias cuando el indicador de errores está activado.#pragma pack
específicamente se usa para indicar que la estructura que se está empaquetando no debe tener sus miembros alineados. Es útil cuando tiene una interfaz asignada de memoria a una pieza de hardware y necesita poder controlar exactamente dónde apuntan los diferentes miembros de la estructura. En particular, no es una buena optimización de velocidad, ya que la mayoría de las máquinas son mucho más rápidas en el manejo de datos alineados.fuente
Le dice al compilador el límite para alinear objetos en una estructura. Por ejemplo, si tengo algo como:
Con una típica máquina de 32 bits, que te normalmente "quiere" para tener 3 bytes de relleno entre
a
yb
para queb
aterrizarán en un límite de 4 bytes para maximizar su velocidad de acceso (y eso es lo que sucede normalmente por defecto).Sin embargo, si tiene que coincidir con una estructura definida externamente, desea asegurarse de que el compilador presenta su estructura exactamente de acuerdo con esa definición externa. En este caso, se puede dar el compilador una
#pragma pack(1)
contarla no insertar ningún relleno entre los miembros - si la definición de la estructura incluye relleno entre los miembros, lo inserta de manera explícita (por ejemplo, por lo general con los miembros nombradosunusedN
oignoreN
, o algo por el estilo orden).fuente
b
en un límite de 4 bytes significa que el procesador puede cargarlo emitiendo una sola carga de 4 bytes. Aunque depende un poco del procesador, si está en un límite extraño, existe una buena posibilidad de que al cargarlo requiera que el procesador emita dos instrucciones de carga separadas, luego use una palanca de cambios para unir esas piezas. La penalización típica es del orden de una carga 3 veces más lenta de ese elemento.Los elementos de datos (por ejemplo, miembros de clases y estructuras) generalmente se alinean en los límites WORD o DWORD para los procesadores de generación actuales con el fin de mejorar los tiempos de acceso. Recuperar un DWORD en una dirección que no es divisible por 4 requiere al menos un ciclo de CPU adicional en un procesador de 32 bits. Entonces, si tiene, por ejemplo, tres miembros char
char a, b, c;
, en realidad tienden a ocupar 6 o 12 bytes de almacenamiento.#pragma
le permite anular esto para lograr un uso de espacio más eficiente, a expensas de la velocidad de acceso o para la consistencia de los datos almacenados entre diferentes objetivos del compilador. Me divertí mucho con esta transición del código de 16 bits a 32 bits; Espero que portar código de 64 bits cause el mismo tipo de dolores de cabeza para algunos códigos.fuente
char a,b,c;
generalmente tomará 3 o 4 bytes de almacenamiento (al menos en x86), eso se debe a que su requisito de alineación es de 1 byte. Si no fuera así, ¿cómo trataríaschar str[] = "foo";
? El acceso a achar
es siempre una simple máscara de captación de desplazamiento, mientras que el acceso a unaint
puede ser fetch-fetch-merge o simplemente fetch, dependiendo de si está alineado o no.int
tiene (en x86) una alineación de 32 bits (4 bytes) porque de lo contrario obtendría (digamos) medioint
en unoDWORD
y medio en el otro, y eso requeriría dos búsquedas.El compilador podría alinear miembros en estructuras para lograr el máximo rendimiento en la plataforma determinada.
#pragma pack
La directiva le permite controlar esa alineación. Por lo general, debe dejarlo de forma predeterminada para un rendimiento óptimo. Si necesita pasar una estructura a la máquina remota, generalmente la usará#pragma pack 1
para excluir cualquier alineación no deseada.fuente
Un compilador puede colocar miembros de la estructura en límites de bytes particulares por razones de rendimiento en una arquitectura particular. Esto puede dejar relleno no utilizado entre los miembros. El embalaje de estructura obliga a los miembros a ser contiguos.
Esto puede ser importante, por ejemplo, si necesita una estructura que se ajuste a un archivo en particular o un formato de comunicación donde los datos que necesita estén en posiciones específicas dentro de una secuencia. Sin embargo, dicho uso no se ocupa de los problemas de endianidad, por lo que, aunque se utiliza, puede no ser portátil.
También puede superponer exactamente la estructura de registro interno de algunos dispositivos de E / S, como un controlador UART o USB, por ejemplo, para que el acceso al registro sea a través de una estructura en lugar de direcciones directas.
fuente
Es probable que solo desee usar esto si estuviera codificando en algún hardware (por ejemplo, un dispositivo mapeado en memoria) que tuviera requisitos estrictos para ordenar y alinear registros.
Sin embargo, esto parece una herramienta bastante contundente para lograr ese fin. Un mejor enfoque sería codificar un mini controlador en ensamblador y darle una interfaz de llamada C en lugar de buscar con este pragma.
fuente
Lo he usado en código antes, aunque solo para interactuar con código heredado. Esta era una aplicación Mac OS X Cocoa que necesitaba cargar archivos de preferencias de una versión anterior de Carbon (que en sí misma era compatible con la versión original de M68k System 6.5 ... ya se entiende). Los archivos de preferencias en la versión original eran un volcado binario de una estructura de configuración, que utilizaba el
#pragma pack(1)
para evitar ocupar espacio adicional y ahorrar basura (es decir, los bytes de relleno que de otro modo estarían en la estructura).Los autores originales del código también habían usado
#pragma pack(1)
para almacenar estructuras que se usaban como mensajes en la comunicación entre procesos. Creo que la razón aquí fue evitar la posibilidad de tamaños de relleno desconocidos o modificados, ya que el código a veces miraba una parte específica de la estructura del mensaje contando una cantidad de bytes desde el principio (ewww).fuente
He visto a personas usarlo para asegurarse de que una estructura tome una línea de caché completa para evitar el intercambio falso en un contexto multiproceso. Si va a tener una gran cantidad de objetos que se empaquetarán de manera holgada por defecto, podría ahorrar memoria y mejorar el rendimiento de la caché para empacarlos más, aunque el acceso a la memoria no alineado generalmente ralentizará las cosas, por lo que puede haber una desventaja.
fuente
Tenga en cuenta que hay otras formas de lograr la consistencia de datos que ofrece #pragma pack (por ejemplo, algunas personas usan #pragma pack (1) para estructuras que deben enviarse a través de la red). Por ejemplo, vea el siguiente código y su salida posterior:
El resultado es el siguiente: sizeof (struct a): 15, sizeof (struct b): 24 sizeof (twoa): 30, sizeof (twob): 48
Observe cómo el tamaño de la estructura a es exactamente el recuento de bytes, pero la estructura b tiene relleno agregado (consulte esto para obtener detalles sobre el relleno). Al hacer esto en lugar del paquete #pragma, puede tener el control de convertir el "formato de cable" en los tipos apropiados. Por ejemplo, "char two [2]" en un "short int", etc.
fuente
sizeof
devuelve unsize_t
que debe imprimirse usando%zu
. El uso del especificador de formato incorrecto invoca un comportamiento indefinido¿Por qué uno quiere usarlo?
Para reducir la memoria de la estructura.
¿Por qué uno no debería usarlo?
fuente