StructLayoutAttribute.Pack Feld

Definition

Steuert die Ausrichtung der Datenfelder einer Klasse oder Struktur im Speicher.

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

Feldwert

Hinweise

Das Pack Feld steuert die Ausrichtung der Felder eines Typs im Arbeitsspeicher. Dies wirkt sich auf die LayoutKind.Sequential Eigenschaft aus. Der Wert gibt die Standardverpackungsgröße für die aktuelle Plattform an. Der Wert von Pack muss 0, 1, 2, 4, 8, 16, 32, 64 oder 128 sein. Der Standardwert ist 0.

Die Felder einer Typinstanz werden mithilfe der folgenden Regeln ausgerichtet:

  • Die Ausrichtung eines Typs ist die Größe des größten Elements (z. B. 1, 2, 4 oder 8 Bytes) oder die angegebene Packgröße, je nachdem, welcher Wert kleiner ist.
  • Jedes Feld muss an Feldern seiner eigenen Größe oder der Ausrichtung des Typs ausgerichtet sein, je nachdem, welcher Wert kleiner ist. Da die Standardausrichtung des Typs die Größe des größten Elements ist, das größer oder gleich allen anderen Feldlängen ist, bedeutet dies in der Regel, dass Felder nach ihrer Größe ausgerichtet werden. Selbst wenn das größte Feld in einem Typ eine 64-Bit-Ganzzahl (8 Byte) ist oder das Feld Pack auf 8 festgelegt ist, Byte richten Felder an 1-Byte-Grenzen, Int16 Felder an 2-Byte-Grenzen und Int32 Felder an 4-Byte-Grenzen aus.
  • Die Auffüllung wird zwischen den Feldern hinzugefügt, um die Ausrichtungsanforderungen zu erfüllen.

Betrachten Sie beispielsweise die folgende Struktur, die aus zwei Byte Feldern und einem Int32 Feld besteht, wenn sie mit verschiedenen Werten für das Pack Feld verwendet wird.

using System;

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

Wichtig

Um die C#-Beispiele erfolgreich zu kompilieren, müssen Sie den /unsafe Compilerschalter angeben.

Wenn Sie die Standardverpackungsgröße angeben, beträgt die Größe der Struktur 8 Bytes. Die beiden Bytes belegen die ersten beiden Bytes des Arbeitsspeichers, da Bytes an Ein-Byte-Grenzen ausgerichtet sein müssen. Da die Standardausrichtung des Typs 4 Bytes ist, was die Größe der größten Felder ist, i3gibt es zwei Bytes Auffüllung gefolgt vom Ganzzahlfeld.

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

Wenn Pack auf 2 festgelegt ist, beträgt die Größe der Struktur 6 Bytes. Wie zuvor belegen die beiden Bytes die ersten beiden Bytes des Arbeitsspeichers. Da Felder jetzt an 2-Byte-Grenzen ausgerichtet sind, gibt es keine Auffüllung zwischen dem zweiten Byte und der ganzzahligen Zahl.

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

Wenn Pack auf 4 festgelegt ist, ist die Größe der Struktur identisch mit dem Standardfall, in dem die Ausrichtung des Typs durch die Größe des größten Felds definiert wurde, i3das 4 ist.

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

Wenn Pack auf 8 festgelegt ist, ist die Größe der Struktur immer noch die gleiche wie im Standardfall, da das i3 Feld an einer 4-Byte-Grenze ausgerichtet wird, die kleiner als die im Feld Pack angegebene 8-Byte-Grenze ist.

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

Um ein weiteres Beispiel zu nehmen, betrachten Sie die folgende Struktur, die aus zwei Bytefeldern, einem 32-Bit-Ganzzahlfeld mit Vorzeichen, einem Bytearray mit einem einzelnen Element und einem Dezimalwert besteht. Bei der Standardverpackungsgröße beträgt die Größe der Struktur 28 Bytes in .NET Framework und 32 Bytes in .NET 5 und höher. Die beiden Bytes belegen die ersten beiden Bytes des Arbeitsspeichers, gefolgt von zwei Bytes Des Abstands, gefolgt von der ganzen Zahl. Als Nächstes folgt das Ein-Byte-Array, gefolgt von drei Byte des Abstands. Da ein Dezimalwert aus mehreren Feldern besteht, basiert die Ausrichtung auf dem größten seiner Felder und nicht auf der Größe der Decimal Struktur als Ganzes. In .NET 5 und höheren Versionen besteht die Decimal Struktur aus zwei Int32 Feldern und einem 8-Byte-Feld, sodass das Decimal Feld d5 an einer 8-Byte-Grenze ausgerichtet wird. In .NET Framework besteht die Decimal Struktur aus vier Int32 Feldern, sodass das Decimal Feld d5 an einer 4-Byte-Grenze ausgerichtet wird.

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

Wenn Pack auf 2 festgelegt ist, beträgt die Größe der Struktur 24 Bytes. Im Vergleich zur Standardausrichtung wurden die zwei Bytes des Abstands zwischen den beiden Bytes und der ganzen Zahl entfernt, da die Ausrichtung des Typs jetzt 4 statt 2 ist. Und die drei Bytes des Abstands nach a4 wurden durch ein Byte des Auffüllens ersetzt, da d5 sie jetzt an einer 2-Byte-Grenze und nicht an einer 4-Byte-Grenze ausgerichtet werden.

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

Wenn Pack auf 16 festgelegt ist, ist die Größe der Struktur identisch mit der im Standardfall, da alle Ausrichtungsanforderungen in dieser Struktur kleiner als 16 sind.

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

Das Pack Feld wird häufig verwendet, wenn Strukturen während schreibvorgängen von Datenträgern und Netzwerken exportiert werden. Das Feld wird auch häufig bei Plattformaufruf- und Interoperabilitätsvorgängen verwendet.

Gelegentlich wird das Feld verwendet, um den Speicherbedarf zu reduzieren, indem eine engere Packungsgröße erzeugt wird. Diese Nutzung erfordert jedoch eine sorgfältige Berücksichtigung der tatsächlichen Hardwareeinschränkungen und kann tatsächlich die Leistung beeinträchtigen.

Gilt für: