2010-07-23 17 views
147

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

Comprobé el MSDN page, que ofrecía alguna información, pero esperaba escuchar más de personas con experiencia. Lo he visto en el código antes, aunque parece que ya no puedo encontrarlo.

+0

Fuerza una alineación particular/embalaje de una estructura, pero al igual que todas las directivas pragma' # 'que son definidos por la implementación. – dreamlax

+0

'A mod s = 0' donde A es la dirección ys es el tamaño del tipo de datos; esto verifica si los datos no están desalineados. – legends2k

Respuesta

284

#pragma pack indica al compilador que empaquete los miembros de la estructura con una alineación particular. La mayoría de los compiladores, 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 arquitecturas asociadas con el acceso a variables que no están alineadas correctamente. Por ejemplo, dada 4 bytes enteros y la siguiente estructura:

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

El compilador podría optar por poner la estructura en la memoria de esta manera:

| 1 | 2 | 3 | 4 | 

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

y sizeof(Test) habría 4 × 3 = 12 , a pesar de que solo contiene 6 bytes de datos. El caso de uso más común para el #pragma (que yo sepa) es cuando se trabaja con dispositivos de hardware donde necesita asegurarse de que el compilador no inserta relleno en los datos y cada miembro sigue el anterior. Con #pragma pack(1), la estructura anterior se presenta como esto:

| 1 | 

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

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

Con #pragma pack(2), la struct anterior sería diseñada como este:

| 1 | 2 | 

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

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

+48

Podría valer la pena agregar los inconvenientes del embalaje. (los accesos de objeto no alineados son lentos en el * mejor * caso, pero causarán errores en algunas plataformas.) – jalf

+9

Parece que la "penalización de rendimiento" de las alineaciones mencionada podría ser realmente un beneficio en algunos sistemas http://danluu.com/3c-conflict /. –

+1

¿Qué le daría '#pragma pack (2)'? –

1

Es probable que solo desee utilizar esto si estuviera codificando en algún hardware (por ejemplo, un dispositivo mapeado en 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.

+0

De hecho, lo uso bastante para ahorrar espacio en tablas grandes a las que no se accede con frecuencia. Allí, es solo para ahorrar espacio y no para una alineación estricta. (Acabo de votar por usted, por cierto. Alguien le ha dado un voto negativo.) –

13

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

struct foo { 
    char a; 
    int b; 
}; 

Con una típica máquina de 32 bits, que te normalmente "quiere" para tener 3 bytes de relleno entre a y bb modo que aterrizarán en una 4- límite de bytes para maximizar su velocidad de acceso (y eso es lo que normalmente ocurrirá de manera predeterminada).

Si, sin embargo, tiene que coincidir con una estructura definida externamente, quiere asegurarse de que el compilador establece su estructura exactamente de acuerdo con esa definición externa. En este caso, puede darle al compilador un #pragma pack(1) para indicar no para insertar cualquier relleno entre los miembros: si la definición de la estructura incluye relleno entre los miembros, la inserta explícitamente (p. Ej., Típicamente con miembros llamados unusedN o ignoreN , o algo de ese orden).

+0

"normalmente" querría "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 ", ¿cómo sería posible tener 3 bytes de relleno para maximizar la velocidad de acceso? – Ashwin

+6

@Ashwin: Colocar 'b' en un límite de 4 bytes significa que el procesador puede cargarlo emitiendo una sola carga de 4 bytes. Aunque depende en cierto modo del procesador, si se encuentra en un límite impar, hay una buena probabilidad de que cargarlo requiera que el procesador emita dos instrucciones de carga separadas, luego use una palanca de cambios para juntar esas piezas. La penalización típica es del orden de 3 veces la carga más lenta de ese artículo. –

+0

...Si observa el código de ensamblaje para la lectura alineada y desalineada, la lectura alineada suele ser una sola tecla mnemotécnica. La lectura desalineada puede ser de 10 líneas de ensamblaje fácilmente, ya que corta el entero, lo selecciona byte por byte y lo ubica en las ubicaciones correctas del registro. –

7

elementos de datos (por ejemplo, miembros de las clases y estructuras) son típicamente alineados 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 char a, b, c;, en realidad tienden a tomar 6 o 12 bytes de almacenamiento.

#pragma le 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 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 los mismos tipos de dolores de cabeza a algunos códigos.

+0

En realidad, 'char a, b, c; 'generalmente tomará 3 o 4 bytes de almacenamiento (al menos en x86), eso es porque su requisito de alineación es de 1 byte. Si no fuera así, ¿cómo tratarías con 'char str [] =" foo ";'? El acceso a un 'char' es siempre un simple fetch-shift-mask, mientras que el acceso a un' int' 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) la mitad de' int' en una 'DWORD' y la mitad en la otra, y eso requeriría dos búsquedas. –

2

Un compilador puede ubicar los miembros de la estructura en determinados límites de bytes por motivos de rendimiento en una arquitectura 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 la estructura de registro interno de algunos dispositivos 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.

2

El compilador podría alinear miembros en estructuras para lograr el máximo rendimiento en la plataforma determinada. La directiva #pragma pack le 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.

17

#pragma se utiliza para enviar mensajes no portátiles (como en este compilador únicamente) al compilador. 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 compila 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.

+8

Para deshacer luego haz esto: #pragma pack (push, 1) y #pragma pack (pop) – malhal

1

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 más espacio y guardar basura (es decir, los bytes de relleno que de lo contrario estarían en la estructura).

Los autores originales del código también han utilizado #pragma pack(1) para almacenar 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).

1

He visto a personas 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.

0

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 estructuras que deben enviarse a través de la red). Por ejemplo, ver el siguiente código y su posterior salida:

#include <stdio.h> 

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)); 
} 

La salida es la siguiente: sizeof (struct a): 15, sizeof (struct b): 24 sizeof (Dosa): 30, sizeof (twob): 48

Observe cómo el tamaño de la estructura a es exactamente lo que es el conteo de bytes, pero struct b tiene relleno agregado (vea this 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.

+0

No, está mal. Si miras la posición en memoria de b.two, no es un byte después de b.one (el compilador puede (y con frecuencia) alineará b.two para que esté alineado con el acceso de palabra). Para a.two, es exactamente un byte después de a.one. Si necesita acceder a .todos como un int corto, debe tener 2 alternativas, ya sea usar una unión (pero esto generalmente falla si tiene problemas de endianness), o descomprimir/convertir por código (usando la función ntohX apropiada) – xryl669

2
  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 compilación.
  3. # pragma pack (push [, n]) empuja la configuración de alineación actual en una pila 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 pila interna (y elimina esa entrada de pila). Tenga en cuenta que #pragma pack ([n]) no influye en esta pila 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]; 
};