StructLayoutAttribute.Pack Champ

Définition

Contrôle l'alignement des champs de données d'une classe ou d'une structure en mémoire.

public: int Pack;
public int Pack;
val mutable Pack : int
Public Pack As Integer 

Valeur de champ

Remarques

Le Pack champ contrôle l’alignement des champs d’un type en mémoire. Elle affecte LayoutKind.Sequential. Par défaut, la valeur est 0, ce qui indique la taille d’empaquetage par défaut pour la plateforme actuelle. La valeur de Pack doit être 0, 1, 2, 4, 8, 16, 32, 64 ou 128 :

Les champs d’un type instance sont alignés à l’aide des règles suivantes :

  • L’alignement du type correspond à la taille de son plus grand élément (1, 2, 4, 8, etc., octets) ou à la taille de compression spécifiée, selon la taille la plus petite.

  • Chaque champ doit s’aligner sur les champs de sa propre taille (1, 2, 4, 8, etc., octets) ou de l’alignement du type, selon la taille la plus petite. Étant donné que l’alignement par défaut du type est la taille de son élément le plus grand, qui est supérieur ou égal à toutes les autres longueurs de champs, cela signifie généralement que les champs sont alignés par leur taille. Par exemple, même si le champ le plus grand d’un type est un entier de 64 bits (8 octets) ou si le champ Pack a la valeur 8, Byte les champs s’alignent sur des limites de 1 octet, Int16 les champs s’alignent sur des limites de 2 octets et Int32 les champs s’alignent sur des limites de 4 octets.

  • Le remplissage est ajouté entre les champs pour répondre aux exigences d’alignement.

Par exemple, considérez la structure suivante, qui se compose de deux Byte champs et d’un Int32 champ, lorsqu’elle est utilisée avec différentes valeurs pour le Pack champ.

using System;

struct ExampleStruct
{
   public byte b1;
   public byte b2;
   public int i3;
}

Important

Pour compiler correctement les exemples C#, vous devez spécifier le commutateur de /unsafe compilateur.

Si vous spécifiez la taille de compression par défaut, la taille de la structure est de 8 octets. Les deux octets occupent les deux premiers octets de mémoire, car les octets doivent s’aligner sur les limites d’un octet. Étant donné que l’alignement par défaut du type est de 4 octets, ce qui correspond à la taille de ses champs les plus volumineux, i3, il existe deux octets de remplissage suivis du champ entier.

using System;
using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential, Pack=0)]
struct ExampleStruct
{
   public byte b1;
   public byte b2;
   public int i3;
}

public class Example
{
   public unsafe static void Main()
   {

      ExampleStruct ex = new ExampleStruct();
      byte* addr = (byte*) &ex;
      Console.WriteLine("Size:      {0}", sizeof(ExampleStruct));
      Console.WriteLine("b1 Offset: {0}", &ex.b1 - addr);
      Console.WriteLine("b2 Offset: {0}", &ex.b2 - addr);
      Console.WriteLine("i3 Offset: {0}", (byte*) &ex.i3 - addr);
   }
}
// The example displays the following output:
//       Size:      8
//       b1 Offset: 0
//       b2 Offset: 1
//       i3 Offset: 4

Si Pack a la valeur 2, la taille de la structure est de 6 octets. Comme précédemment, les deux octets occupent les deux premiers octets de mémoire. Étant donné que les champs s’alignent désormais sur des limites de 2 octets, il n’y a aucun remplissage entre le deuxième octet et l’entier.

using System;
using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential, Pack=2)]
struct ExampleStruct
{
   public byte b1;
   public byte b2;
   public int i3;
}

public class Example
{
   public unsafe static void Main()
   {

      ExampleStruct ex = new ExampleStruct();
      byte* addr = (byte*) &ex;
      Console.WriteLine("Size:      {0}", sizeof(ExampleStruct));
      Console.WriteLine("b1 Offset: {0}", &ex.b1 - addr);
      Console.WriteLine("b2 Offset: {0}", &ex.b2 - addr);
      Console.WriteLine("i3 Offset: {0}", (byte*) &ex.i3 - addr);
   }
}
// The example displays the following output:
//       Size:      6
//       b1 Offset: 0
//       b2 Offset: 1
//       i3 Offset: 2

Si Pack a la valeur 4, la taille de la structure est la même que dans le cas par défaut, où l’alignement du type a été défini par la taille de son champ le plus grand, i3, qui est 4.

using System;
using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential, Pack=4)]
struct ExampleStruct
{
   public byte b1;
   public byte b2;
   public int i3;
}

public class Example
{
   public unsafe static void Main()
   {

      ExampleStruct ex = new ExampleStruct();
      byte* addr = (byte*) &ex;
      Console.WriteLine("Size:      {0}", sizeof(ExampleStruct));
      Console.WriteLine("b1 Offset: {0}", &ex.b1 - addr);
      Console.WriteLine("b2 Offset: {0}", &ex.b2 - addr);
      Console.WriteLine("i3 Offset: {0}", (byte*) &ex.i3 - addr);
   }
}
// The example displays the following output:
//       Size:      8
//       b1 Offset: 0
//       b2 Offset: 1
//       i3 Offset: 4

Si Pack a la valeur 8, la taille de la structure est toujours la même que dans le cas par défaut, car le i3 champ s’aligne sur une limite de 4 octets, qui est inférieure à la limite de 8 octets spécifiée par le champ Pack.

using System;
using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential, Pack=8)]
struct ExampleStruct
{
   public byte b1;
   public byte b2;
   public int i3;
}

public class Example
{
   public unsafe static void Main()
   {

      ExampleStruct ex = new ExampleStruct();
      byte* addr = (byte*) &ex;
      Console.WriteLine("Size:      {0}", sizeof(ExampleStruct));
      Console.WriteLine("b1 Offset: {0}", &ex.b1 - addr);
      Console.WriteLine("b2 Offset: {0}", &ex.b2 - addr);
      Console.WriteLine("i3 Offset: {0}", (byte*) &ex.i3 - addr);
   }
}
// The example displays the following output:
//       Size:      8
//       b1 Offset: 0
//       b2 Offset: 1
//       i3 Offset: 4

Pour prendre un autre exemple, considérez la structure suivante, qui se compose de deux champs d’octets, d’un champ entier signé 32 bits, d’un tableau d’octets à élément unique et d’une valeur décimale. Avec la taille de compression par défaut, la taille de la structure est de 28 octets. Les deux octets occupent les deux premiers octets de mémoire, suivis de deux octets de remplissage, suivis de l’entier. Ensuite se trouve le tableau d’un octet, suivi de trois octets de remplissage. Enfin, le Decimal champ, d5, s’aligne sur une limite de 4 octets, car une valeur décimale se compose de quatre Int32 champs, de sorte que son alignement est basé sur la taille du plus grand de ses champs plutôt que sur la taille de la Decimal structure dans son ensemble.

using System;
using System.Runtime.InteropServices;

unsafe struct ExampleStruct2
{

   public byte b1;
   public byte b2;
   public int i3;
   public fixed byte a4[1];
   public decimal d5;
}

public class Example
{
   public unsafe static void Main()
   {

      ExampleStruct2 ex = new ExampleStruct2();
      byte* addr = (byte*) &ex;
      Console.WriteLine("Size:      {0}", sizeof(ExampleStruct2));
      Console.WriteLine("b1 Offset: {0}", &ex.b1 - addr);
      Console.WriteLine("b2 Offset: {0}", &ex.b2 - addr);
      Console.WriteLine("i3 Offset: {0}", (byte*) &ex.i3 - addr);
      Console.WriteLine("a4 Offset: {0}", ex.a4 - addr);
      Console.WriteLine("d5 Offset: {0}", (byte*) &ex.d5 - addr);
   }
}
// The example displays the following output:
//       Size:      28
//       b1 Offset: 0
//       b2 Offset: 1
//       i3 Offset: 4
//       a4 Offset: 8
//       d5 Offset: 12

Si Pack a la valeur 2, la taille de la structure est de 24 octets. Par rapport à l’alignement par défaut, les deux octets de remplissage entre les deux octets et l’entier ont été supprimés, car l’alignement du type est désormais de 4 au lieu de 2. Et les trois octets de remplissage après a4 ont été remplacés par un octet de remplissage, car d5 s’alignent désormais sur une limite de 2 octets plutôt que sur une limite de 4 octets.

using System;
using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential, Pack = 2)]
unsafe struct ExampleStruct2
{

   public byte b1;
   public byte b2;
   public int i3;
   public fixed byte a4[1];
   public decimal d5;
}

public class Example
{
   public unsafe static void Main()
   {

      ExampleStruct2 ex = new ExampleStruct2();
      byte* addr = (byte*) &ex;
      Console.WriteLine("Size:      {0}", sizeof(ExampleStruct2));
      Console.WriteLine("b1 Offset: {0}", &ex.b1 - addr);
      Console.WriteLine("b2 Offset: {0}", &ex.b2 - addr);
      Console.WriteLine("i3 Offset: {0}", (byte*) &ex.i3 - addr);
      Console.WriteLine("a4 Offset: {0}", ex.a4 - addr);
      Console.WriteLine("d5 Offset: {0}", (byte*) &ex.d5 - addr);
   }
}
// The example displays the following output:
//       Size:      24
//       b1 Offset: 0
//       b2 Offset: 1
//       i3 Offset: 2
//       a4 Offset: 6
//       d5 Offset: 8

Si Pack a la valeur 8, la taille de la structure est la même que dans le cas par défaut, car toutes les exigences d’alignement de cette structure sont inférieures à 8.

using System;
using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential, Pack = 8)]
unsafe struct ExampleStruct2
{

   public byte b1;
   public byte b2;
   public int i3;
   public fixed byte a4[1];
   public decimal d5;
}

public class Example
{
   public unsafe static void Main()
   {

      ExampleStruct2 ex = new ExampleStruct2();
      byte* addr = (byte*) &ex;
      Console.WriteLine("Size:      {0}", sizeof(ExampleStruct2));
      Console.WriteLine("b1 Offset: {0}", &ex.b1 - addr);
      Console.WriteLine("b2 Offset: {0}", &ex.b2 - addr);
      Console.WriteLine("i3 Offset: {0}", (byte*) &ex.i3 - addr);
      Console.WriteLine("a4 Offset: {0}", ex.a4 - addr);
      Console.WriteLine("d5 Offset: {0}", (byte*) &ex.d5 - addr);
   }
}
// The example displays the following output:
//       Size:      28
//       b1 Offset: 0
//       b2 Offset: 1
//       i3 Offset: 4
//       a4 Offset: 8
//       d5 Offset: 12

Le Pack champ est fréquemment utilisé lorsque des structures sont exportées pendant les opérations d’écriture sur disque et réseau. Le champ est également fréquemment utilisé lors des opérations d’appel de plateforme et d’interopérabilité.

Parfois, le champ est utilisé pour réduire les besoins en mémoire en produisant une taille d’emballage plus serrée. Toutefois, cette utilisation nécessite un examen minutieux des contraintes matérielles réelles et peut effectivement dégrader les performances.

S’applique à