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. Cela affecte la LayoutKind.Sequential propriété. La valeur 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. La valeur par défaut est 0.

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

  • L’alignement d’un type correspond à la taille de son plus grand élément (par exemple, 1, 2, 4 ou 8 octets) ou à la taille d’empaquetage spécifiée, selon la plus petite des deux.
  • Chaque champ doit s’aligner sur des champs de sa propre taille 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 champ, 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 les limites d’un 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 d’empaquetage 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 plus grands champs, i3il existe deux octets de remplissage suivis du champ entier.

using System;
using System.Runtime.InteropServices;

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

public class Example1
{
    public unsafe static void Main()
    {
        ExampleStruct1 ex = new();
        byte* addr = (byte*)&ex;
        Console.WriteLine("Size:      {0}", sizeof(ExampleStruct1));
        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 est défini sur 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 pas de remplissage entre le deuxième octet et l’entier.

using System;
using System.Runtime.InteropServices;

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

public class Example2
{
    public unsafe static void Main()
    {
        ExampleStruct2 ex = new();
        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);
    }
}
// The example displays the following output:
//       Size:      6
//       b1 Offset: 0
//       b2 Offset: 1
//       i3 Offset: 2

Si Pack est défini sur 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 plus grand champ, i3, qui est 4.

using System;
using System.Runtime.InteropServices;

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

public class Example3
{
    public unsafe static void Main()
    {
        ExampleStruct3 ex = new();
        byte* addr = (byte*)&ex;
        Console.WriteLine("Size:      {0}", sizeof(ExampleStruct3));
        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 est défini sur 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 plus petite que la limite de 8 octets spécifiée par le champ Pack.

using System;
using System.Runtime.InteropServices;

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

public class Example4
{
    public unsafe static void Main()
    {
        ExampleStruct4 ex = new();
        byte* addr = (byte*)&ex;
        Console.WriteLine("Size:      {0}", sizeof(ExampleStruct4));
        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, un champ entier signé 32 bits, un tableau d’octets à élément unique et une valeur décimale. Avec la taille d’empaquetage par défaut, la taille de la structure est de 28 octets dans .NET Framework et de 32 octets dans .NET 5+. Les deux octets occupent les deux premiers octets de mémoire, suivis de deux octets de remplissage, suivis de l’entier. Voici le tableau d’un octet, suivi de trois octets de remplissage. Étant donné qu’une valeur décimale se compose de plusieurs champs, l’alignement est basé sur le plus grand de ses champs plutôt que sur la taille de la Decimal structure dans son ensemble. Dans .NET 5 et versions ultérieures, la Decimal structure se compose de deux Int32 champs et d’un champ de 8 octets, de sorte que le Decimal champ, d5, s’aligne sur une limite de 8 octets. Dans .NET Framework, la Decimal structure se compose de quatre Int32 champs, de sorte que le Decimal champ d5 s’aligne sur une limite de 4 octets.

using System;

unsafe struct ExampleStruct5
{

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

public class Example5
{
    public unsafe static void Main()
    {
        ExampleStruct5 ex = new();
        byte* addr = (byte*)&ex;
        Console.WriteLine("Size:      {0}", sizeof(ExampleStruct5));
        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:
//
// .NET 5+:
//       Size:      32
//       b1 Offset: 0
//       b2 Offset: 1
//       i3 Offset: 4
//       a4 Offset: 8
//       d5 Offset: 16
//
// .NET Framework:
//       Size:      28
//       b1 Offset: 0
//       b2 Offset: 1
//       i3 Offset: 4
//       a4 Offset: 8
//       d5 Offset: 12

Si Pack est défini sur 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 maintenant s’aligne 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 ExampleStruct6
{

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

public class Example6
{
    public unsafe static void Main()
    {
        ExampleStruct6 ex = new();
        byte* addr = (byte*)&ex;
        Console.WriteLine("Size:      {0}", sizeof(ExampleStruct6));
        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 est défini sur 16, 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 à 16.

using System;
using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential, Pack = 16)]
unsafe struct ExampleStruct7
{

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

public class Example7
{
    public unsafe static void Main()
    {
        ExampleStruct7 ex = new();
        byte* addr = (byte*)&ex;
        Console.WriteLine("Size:      {0}", sizeof(ExampleStruct7));
        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:
//
// .NET 5+:
//       Size:      32
//       b1 Offset: 0
//       b2 Offset: 1
//       i3 Offset: 4
//       a4 Offset: 8
//       d5 Offset: 16
//
// .NET Framework:
//       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 attentif des contraintes matérielles réelles et peut effectivement dégrader les performances.

S’applique à