# pack effet

Je me demandais si quelqu’un pouvait m’expliquer ce que fait l’instruction de préprocesseur du #pragma pack , et surtout, pourquoi quelqu’un voudrait l’utiliser.

J’ai consulté la page MSDN , qui donnait un aperçu, mais j’espérais entendre plus de personnes expérimentées. Je l’ai déjà vu dans le code, même si je n’arrive plus à trouver où.

#pragma pack indique au compilateur de regrouper les membres de la structure avec un alignement particulier. Lorsque vous déclarez une structure, la plupart des compilateurs insèrent un remplissage entre les membres pour s’assurer qu’ils sont alignés sur les adresses appropriées en mémoire (généralement un multiple de la taille du type). Cela évite la pénalité de performance (ou l’erreur absolue) sur certaines architectures associées à l’access à des variables qui ne sont pas correctement alignées. Par exemple, les entiers à 4 octets et la structure suivante:

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

Le compilateur pourrait choisir de placer la structure en mémoire comme ceci:

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

et sizeof(Test) serait 4 × 3 = 12, même s’il ne contient que 6 octets de données. Le cas d’utilisation le plus courant pour #pragma (à ma connaissance) est l’utilisation de périphériques matériels sur lesquels vous devez vous assurer que le compilateur n’insère pas de remplissage dans les données et que chaque membre suit le précédent. Avec #pragma pack(1) , la structure ci-dessus serait disposée comme ceci:

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

Et sizeof(Test) serait 1 × 6 = 6.

Avec #pragma pack(2) , la structure ci-dessus serait disposée comme ceci:

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

Et sizeof(Test) serait 2 × 4 = 8.

#pragma est utilisé pour envoyer des messages non portables (comme dans ce compilateur uniquement) au compilateur. Des choses comme la désactivation de certains avertissements et de certaines structures d’emballage sont des raisons courantes. La désactivation d’avertissements spécifiques est particulièrement utile si vous comstackz avec l’indicateur d’avertissement lorsque l’erreur est activée.

#pragma pack spécifiquement est utilisé pour indiquer que la structure en cours de compilation ne doit pas avoir ses membres alignés. C’est utile lorsque vous avez une interface mappée en mémoire sur un élément matériel et que vous devez pouvoir contrôler exactement l’emplacement des différents membres de la structure. Ce n’est notamment pas une bonne optimisation de la vitesse, car la plupart des machines sont beaucoup plus rapides pour gérer les données alignées.

Il indique au compilateur la limite pour aligner des objects dans une structure. Par exemple, si j’ai quelque chose comme:

 struct foo { char a; int b; }; 

Avec une machine 32 bits typique, vous voudriez normalement avoir 3 octets de remplissage entre a et b pour que b arrive à une limite de 4 octets afin de maximiser sa vitesse d’access (et c’est ce qui se produira généralement par défaut) ).

Si, toutefois, vous devez faire correspondre une structure définie en externe, vous devez vous assurer que le compilateur dispose exactement de votre structure conformément à cette définition externe. Dans ce cas, vous pouvez donner au compilateur un #pragma pack(1) pour lui dire de ne pas insérer de remplissage entre les membres – si la définition de la structure inclut un remplissage entre les membres, vous l’insérez explicitement (par exemple, généralement avec des membres nommés) unusedN ou ignoreN , ou quelque chose sur cette commande).

  1. # pragma pack (n) définit simplement le nouvel alignement.
  2. # pragma pack () définit l’alignement sur celui qui était en vigueur au démarrage de la compilation.
  3. # pragma pack (push [, n]) repousse le paramètre d’alignement actuel sur une stack interne, puis définit éventuellement le nouvel alignement.
  4. # pragma pack (pop) restaure le paramètre d’alignement sur celui enregistré en haut de la stack interne (et supprime cette entrée de stack). Notez que #pragma pack ([n]) n’influence pas cette stack interne; il est donc possible d’avoir #pragma pack (push) suivi de plusieurs instances #pragma pack (n) et finalisé par un seul pack #pragma (pop).

Exemples:

 #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]; }; 

Les éléments de données (par exemple, les membres des classes et des structures) sont généralement alignés sur les limites WORD ou DWORD des processeurs de la génération actuelle afin d’améliorer les temps d’access. La récupération d’un DWORD à une adresse qui n’est pas divisible par 4 nécessite au moins un cycle CPU supplémentaire sur un processeur 32 bits. Donc, si vous avez par exemple trois membres de caractère char a, b, c; , ils ont tendance à prendre 6 ou 12 octets de stockage.

#pragma vous permet de remplacer cela pour optimiser l’utilisation de l’espace, au désortingment de la vitesse d’access ou de la cohérence des données stockées entre les différentes cibles du compilateur. Je me suis beaucoup amusé avec cette transition du code 16 bits au code 32 bits; Je m’attends à ce que le portage en code 64 bits provoque le même type de maux de tête pour certains codes.

Un compilateur peut placer des membres de structure sur des limites d’octet particulières pour des raisons de performances sur une architecture particulière. Cela peut laisser un remplissage inutilisé entre les membres. La structure d’emballage oblige les membres à être contigus.

Cela peut être important, par exemple, si vous avez besoin d’une structure pour se conformer à un fichier ou à un format de communication particulier où les données dont vous avez besoin doivent se trouver à des positions spécifiques dans une séquence. Cependant, cette utilisation ne permet pas de résoudre les problèmes d’endianisation, bien qu’utilisée, elle peut ne pas être portable.

Il est également possible de superposer exactement la structure de registre interne de certains périphériques E / S, tels que les contrôleurs UART ou USB, afin que l’enregistrement des access se fasse par une structure plutôt que par des adresses directes.

Le compilateur pourrait aligner les membres dans les structures pour obtenir des performances maximales sur certaines plates-formes. #pragma pack directive #pragma pack vous permet de contrôler cet alignement. Habituellement, vous devez le laisser par défaut pour des performances optimales. Si vous devez transmettre une structure à la machine distante, vous utiliserez généralement #pragma pack 1 pour exclure tout alignement indésirable.

Vous ne voudrez probablement l’utiliser que si vous codez sur du matériel (par exemple, un périphérique mappé en mémoire) ayant des exigences ssortingctes en matière de classement et d’alignement des registres.

Cependant, cela ressemble à un outil assez brutal pour atteindre cet objective. Une meilleure approche serait de coder un mini-pilote dans l’assembleur et de lui donner une interface d’appel C plutôt que de fouiller avec ce pragma.

Je l’ai déjà utilisé dans le code, mais uniquement pour interagir avec le code existant. C’était une application Mac OS X Cocoa qui devait charger des fichiers de préférences d’une version antérieure de Carbon (qui était elle-même rétro-compatible avec la version originale de M68k System 6.5 … vous avez l’idée). Les fichiers de préférences dans la version d’origine étaient un fichier binary d’une structure de configuration, utilisant le #pragma pack(1) pour éviter de prendre de l’espace supplémentaire et de sauvegarder les fichiers inutiles (c’est-à-dire les octets de remplissage dans la structure).

Les auteurs originaux du code avaient également utilisé #pragma pack(1) pour stocker des structures utilisées comme messages dans une communication inter-processus. Je pense que la raison était d’éviter la possibilité de tailles de remplissage inconnues ou modifiées, car le code regardait parfois une partie spécifique de la structure du message en comptant un nombre d’octets depuis le début (ewww).

J’ai vu des gens l’utiliser pour s’assurer qu’une structure prend une ligne de cache complète pour empêcher un partage erroné dans un contexte multithread. Si vous voulez avoir un grand nombre d’objects qui seront chargés par défaut, cela pourrait économiser de la mémoire et améliorer les performances du cache pour les empaqueter, bien que l’access non aligné à la mémoire ralentisse généralement les choses.

Notez qu’il existe d’autres moyens d’obtenir la cohérence des données qu’offre #pragma pack (par exemple, certaines personnes utilisent #pragma pack (1) pour les structures à envoyer sur le réseau). Par exemple, consultez le code suivant et sa sortie ultérieure:

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

La sortie est la suivante: sizeof (struct a): 15, sizeof (struct b): 24 sizeof (twoa): 30, sizeof (twob): 48

Notez que la taille de struct a correspond exactement au nombre d’octets, mais que struct b a été ajouté (voir ceci pour plus de détails sur le remplissage). En faisant cela par opposition au pack #pragma, vous pouvez contrôler la conversion du “format fil” en types appropriés. Par exemple, “char two [2]” dans un “short int” et cetera.