Efecto paquete #pragma

Me preguntaba si alguien podría explicarme qué hace la statement del preprocesador del #pragma pack y, lo que es más importante, por qué uno querría usarla.

Revisé la página de MSDN , que me brindó algunas ideas, pero esperaba escuchar más de personas con experiencia. Lo he visto en el código antes, aunque parece que ya no puedo encontrarlo.

#pragma pack instruye al comstackdor para empacar miembros de la estructura con una alineación particular. La mayoría de los comstackdores, al declarar una estructura, insertarán 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 del rendimiento (o error absoluto) en algunas architectures asociadas con el acceso a variables que no están alineadas correctamente. Por ejemplo, dados enteros de 4 bytes y la siguiente estructura:

 struct Test { char AA; int BB; char CC; }; 

El comstackdor podría elegir colocar la estructura en la memoria así:

 | 1 | 2 | 3 | 4 | | AA(1) | pad.................. | | BB(1) | BB(2) | BB(3) | BB(4) | | CC(1) | pad.................. | 

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 (que yo sepa) es cuando se trabaja con dispositivos de hardware donde se necesita asegurar que el comstackdor no inserte el relleno en los datos y que cada miembro siga el anterior. Con #pragma pack(1) , la estructura anterior se establecería así:

 | 1 | | AA(1) | | BB(1) | | BB(2) | | BB(3) | | BB(4) | | CC(1) | 

Y sizeof(Test) sería 1 × 6 = 6.

Con #pragma pack(2) , la estructura anterior se distribuiría así:

 | 1 | 2 | | AA(1) | pad.. | | BB(1) | BB(2) | | BB(3) | BB(4) | | CC(1) | pad.. | 

Y sizeof(Test) sería 2 × 4 = 8.

#pragma se usa para enviar mensajes no portátiles (como en este comstackdor solamente) al comstackdor. Cosas como la desactivación de ciertas advertencias y estructuras de embalaje son razones comunes. La desactivación de advertencias específicas es particularmente útil si comstack con las advertencias como bandera de errores activada.

#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 mapeada en la memoria para 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 al tratar con datos alineados.

Le dice al comstackdor el límite para alinear objetos en una estructura. Por ejemplo, si tengo algo como:

 struct foo { char a; int b; }; 

Con una máquina típica de 32 bits, normalmente “querrás” tener 3 bytes de relleno entre a y b para que b aterrice en un límite de 4 bytes para maximizar su velocidad de acceso (y eso es lo que típicamente sucederá de manera predeterminada )

Sin embargo, si tiene que coincidir con una estructura definida externamente, quiere asegurarse de que el comstackdor establece su estructura exactamente de acuerdo con esa definición externa. En este caso, puede darle al comstackdor un #pragma pack(1) para indicarle que no inserte ningún relleno entre los miembros. Si la definición de la estructura incluye relleno entre los miembros, debe insertarlo explícitamente (por ejemplo, típicamente con miembros nombrados unusedN o ignoreN , o algo en ese orden).

  1. # pragma pack (n) simplemente establece la nueva alineación.
  2. # pragma pack () establece la alineación con la que estaba en vigor cuando se inició la comstackción.
  3. # pragma pack (push [, n]) empuja la configuración de alineación actual en una stack interna y luego establece opcionalmente la nueva alineación.
  4. # pragma pack (pop) restaura la configuración de alineación a la guardada en la parte superior de la stack interna (y elimina esa entrada de la stack). Tenga en cuenta que #pragma pack ([n]) no influye en esta stack interna; por lo tanto, es posible tener #pragma pack (push) seguido de múltiples #pragma pack (n) instancias y finalizado por un solo paquete #pragma (pop).

Ejemplos:

 #pragma pack(push, 1) // exact fit - no padding struct MyStruct { char b; int a; int array[2]; }; #pragma pack(pop) //back to whatever the previous packing mode was Or #pragma pack(1) // exact fit - no padding struct MyStruct { char b; int a; int array[2]; }; #pragma pack() //back to whatever the previous packing mode was Or #pragma pack(1) // exact fit - no padding struct MyStruct { char b; int a; int array[2]; }; 

Los elementos de datos (por ejemplo, miembros de clases y estructuras) se alinean normalmente en los límites WORD o DWORD para los procesadores de generación actuales a 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 a, b, c; , en realidad tienden a tomar 6 o 12 bytes de almacenamiento.

#pragma permite anular esto para lograr un uso del espacio más eficiente, a expensas de la velocidad de acceso, o para la coherencia de los datos almacenados entre diferentes objectives del comstackdor. 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 los mismos tipos de dolores de cabeza a algún código.

Un comstackdor puede colocar miembros de la estructura en límites de bytes particulares por razones de rendimiento en una architecture particular. Esto puede dejar relleno sin usar entre los miembros. El empaque de estructura obliga a los miembros a ser contiguos.

Esto puede ser importante, por ejemplo, si necesita una estructura para ajustarse a un archivo o formato de comunicación en particular, donde los datos necesitan que los datos estén en posiciones específicas dentro de una secuencia. Sin embargo, dicho uso no se ocupa de problemas de endianidad, por lo tanto, aunque se use, puede que no sea portátil.

También puede superponerse exactamente a la estructura de registro interno de algún dispositivo de E / S, como un controlador UART o USB, por ejemplo, para que el acceso de registro sea a través de una estructura en lugar de direcciones directas.

El comstackdor podría alinear miembros en estructuras para lograr el máximo rendimiento en la plataforma determinada. #pragma pack directiva #pragma pack permite controlar esa alineación. Por lo general, debe dejarlo de manera predeterminada para un rendimiento óptimo. Si necesita pasar una estructura a la máquina remota, generalmente usará #pragma pack 1 para excluir cualquier alineamiento no deseado.

Probablemente solo desee utilizar esto si estuviera codificando a algún hardware (por ejemplo, un dispositivo mapeado en la memoria) que tuviera requisitos estrictos para ordenar y alinear el registro.

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.

Lo he usado en el código anteriormente, aunque solo para interactuar con el 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 era compatible con la versión original de M68k System 6.5 … se entiende). Los archivos de preferencias en la versión original eran un volcado binario de una estructura de configuración, que usaba el #pragma pack(1) para evitar ocupar espacio extra y guardar 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 las estructuras que se utilizaron como mensajes en la comunicación entre procesos. Creo que la razón aquí fue para 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 al contar una cantidad de bytes desde el inicio (ewww).

He visto a gente usarlo para asegurarse de que una estructura toma una línea de caché completa para evitar compartir falsamente en un contexto multiproceso. Si va a tener una gran cantidad de objetos que van a estar vagamente empaquetados de manera predeterminada, podría ahorrar memoria y mejorar el rendimiento de la memoria caché para empaquetarlos, aunque el acceso a la memoria no alineada ralentizará las cosas, por lo que podría ser un inconveniente.

Tenga en cuenta que hay otras formas de lograr la coherencia de los datos que ofrece #pragma pack (por ejemplo, algunas personas usan #pragma pack (1) para las estructuras que deben enviarse a través de la red). Por ejemplo, vea el siguiente código y su resultado posterior:

 #include  struct a { char one; char two[2]; char eight[8]; char four[4]; }; struct b { char one; short two; long int eight; int four; }; int main(int argc, char** argv) { struct a twoa[2] = {}; struct b twob[2] = {}; printf("sizeof(struct a): %i, sizeof(struct b): %i\n", sizeof(struct a), sizeof(struct b)); printf("sizeof(twoa): %i, sizeof(twob): %i\n", sizeof(twoa), sizeof(twob)); } 

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 lo que es el conteo de bytes, pero la estructura b tiene un relleno agregado (vea esto para detalles sobre el relleno). Al hacer esto en comparación con el paquete #pragma, puede tener el control de convertir el “formato de conexión” en los tipos apropiados. Por ejemplo, “char two [2]” en un “short int”, etcétera.