StructLayoutAttribute.Pack Pole

Definicja

Określa wyrównanie 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 to 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 o własnym rozmiarze lub wyrównaniu 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 innym 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 bajtów) lub pole Pack jest ustawione na 8, pola są wyrównane do granic 1 bajtów, ByteInt16 pola wyrównane do 2-bajtowych granic, a Int32 pola są wyrównane do granic 4-bajtowych.
  • 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 dla Pack pola.

using System;

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

Ważna

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 to 4 bajty, czyli rozmiar największych pól, oznacza to, i3że 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 granic 2 bajtów, nie ma wypeł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, w którym 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 ustawiono 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 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

Aby podjąć inny przykład, 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. Przy domyślnym rozmiarze pakowania rozmiar struktury wynosi 28 bajtów w .NET Framework i 32 bajty w .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 .NET 5 i nowszych wersjach struktura Decimal składa się z dwóch pól Int32 i jednego pola 8-bajtowego, więc pole Decimal, d5, jest wyrównane do granicy 8-bajtowej. W .NET Framework struktura Decimal składa się z czterech pól Int32, więc pole Decimal, 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 teraz jest wyrównany do granicy 2-bajtowej, a nie granicy 4-bajtowej.

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 wywoływania i międzyoperacyjności platformy.

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

Dotyczy