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 to wpływ na .LayoutKind.Sequential Domyślnie wartość to 0 wskazująca domyślny rozmiar pakowania dla bieżącej platformy. Wartość Pack musi być 0, 1, 2, 4, 8, 16, 32, 64 lub 128:

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

  • Wyrównanie typu to rozmiar największego elementu (1, 2, 4, 8 itp., bajty) 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 (1, 2, 4, 8 itp., bajty) 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 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

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 wypełnienia między drugim bajtem a liczbą całkowitą.

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

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 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

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 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

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. 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. Decimal Na koniec pole d5 jest wyrównane do granicy 4-bajtowej, ponieważ wartość dziesiętna składa się z czterech Int32 pól, więc jego wyrównanie opiera się na rozmiarze największych pól, a nie na rozmiarze Decimal struktury jako całości.

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

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 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

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

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