StructLayoutAttribute.Pack Pole

Definicja

Steruje wyrównaniem pól danych klasy lub struktury w pamięci.

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

Wartość pola

Uwagi

Pole Pack steruje wyrównaniem pól typu w pamięci. Ma wpływ na LayoutKind.Sequential właściwość . Wartość wskazuje domyślny rozmiar pakowania dla bieżącej platformy. Wartość Pack musi być 0, 1, 2, 4, 8, 16, 32, 64 lub 128. Wartość domyślna to 0.

Pola wystąpienia typu są wyrównane przy użyciu następujących reguł:

  • Wyrównanie typu to rozmiar największego elementu (na przykład 1, 2, 4 lub 8 bajtów) lub określony rozmiar pakowania, w zależności od tego, co jest mniejsze.
  • Każde pole musi być zgodne z polami własnego rozmiaru lub wyrównaniem typu, w zależności od tego, co jest mniejsze. Ponieważ domyślne wyrównanie typu to rozmiar największego elementu, który jest większy lub równy wszystkim pozostałym długościom pól, zwykle oznacza to, że pola są wyrównane według ich rozmiaru. Na przykład, nawet jeśli największe pole w typie jest 64-bitową liczbą całkowitą (8-bajtową) lub pole Pack jest ustawione na 8, Byte pola są wyrównane do 1-bajtowych granic, Int16 pola wyrównane do 2-bajtowych granic, a Int32 pola są wyrównane do 4-bajtowych granic.
  • Dopełnianie jest dodawane między polami w celu spełnienia wymagań dotyczących wyrównania.

Rozważmy na przykład następującą strukturę, która składa się z dwóch Byte pól i jednego Int32 pola, gdy jest używana z różnymi wartościami pola Pack .

using System;

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

Ważne

Aby pomyślnie skompilować przykłady języka C#, należy określić przełącznik kompilatora /unsafe .

Jeśli określisz domyślny rozmiar pakowania, rozmiar struktury wynosi 8 bajtów. Dwa bajty zajmują pierwsze dwa bajty pamięci, ponieważ bajty muszą być wyrównane do granic jednobajtowych. Ponieważ domyślne wyrównanie typu wynosi 4 bajty, czyli rozmiar największych pól, i3, istnieją dwa bajty wypełnienia, po którym następuje pole liczby całkowitej.

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

Jeśli Pack ustawiono wartość 2, rozmiar struktury wynosi 6 bajtów. Tak jak poprzednio, dwa bajty zajmują pierwsze dwa bajty pamięci. Ponieważ pola są teraz wyrównane do 2-bajtowych granic, nie ma dopełnienia między drugim bajtem a liczbą całkowitą.

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

Jeśli Pack ustawiono wartość 4, rozmiar struktury jest taki sam jak w przypadku domyślnym, gdzie wyrównanie typu zostało zdefiniowane przez rozmiar największego pola, i3czyli 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

Jeśli Pack jest ustawiona wartość 8, rozmiar struktury jest nadal taki sam jak w przypadku domyślnym, ponieważ i3 pole jest wyrównane do granicy 4-bajtowej, która jest mniejsza niż granica 8-bajtowa określona przez pole Pakiet.

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

Aby skorzystać z innego przykładu, rozważ następującą strukturę, która składa się z dwóch pól bajtów, jednego pola liczby całkowitej ze znakiem 32-bitowym, jednej tablicy bajtów i wartości dziesiętnej. W przypadku domyślnego rozmiaru pakowania rozmiar struktury wynosi 28 bajtów w .NET Framework i 32 bajtów na platformie .NET 5+. Dwa bajty zajmują pierwsze dwa bajty pamięci, a następnie dwa bajty wypełnienia, a następnie liczbę całkowitą. Następna jest tablica jednobajtowa, a następnie trzy bajty wypełnienia. Ponieważ wartość dziesiętna składa się z kilku pól, wyrównanie opiera się na największych polach, a nie na rozmiarze Decimal struktury jako całości. W programie .NET 5 i nowszych wersjach Decimal struktura składa się z dwóch Int32 pól i jednego pola 8-bajtowego, więc Decimal pole d5 jest wyrównane do granicy 8-bajtowej. W .NET Framework Decimal struktura składa się z czterech Int32 pól, więc Decimal pole d5 jest wyrównane do granicy 4-bajtowej.

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

Jeśli Pack ustawiono wartość 2, rozmiar struktury wynosi 24 bajty. W porównaniu z domyślnym wyrównaniem dwa bajty wypełnienia między dwoma bajtami i liczbą całkowitą zostały usunięte, ponieważ wyrównanie typu wynosi teraz 4, a nie 2. A trzy bajty wypełnienia po a4 zastąpieniu przez jeden bajt wypełnienia, ponieważ d5 obecnie jest wyrównany do granicy 2-bajtowej, a nie 4-bajtowej granicy.

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

Jeśli Pack ustawiono wartość 16, rozmiar struktury jest taki sam jak w przypadku domyślnym, ponieważ wszystkie wymagania dotyczące wyrównania w tej strukturze są mniejsze niż 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

Pole Pack jest często używane, gdy struktury są eksportowane podczas operacji zapisu dysku i sieci. Pole jest również często używane podczas operacji międzyoperacyjności i wywoływania platformy.

Czasami pole jest używane do zmniejszenia wymagań dotyczących pamięci przez utworzenie ściślejszego rozmiaru pakowania. Jednak to użycie wymaga starannego rozważenia rzeczywistych ograniczeń sprzętowych i może rzeczywiście obniżyć wydajność.

Dotyczy