StructLayoutAttribute.Pack Поле

Определение

Управляет выравниванием полей данных для класса или структуры в памяти.

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

Значение поля

Комментарии

Поле Pack управляет выравниванием полей типа в памяти. Это влияет на LayoutKind.Sequential. По умолчанию значение равно 0, указывая размер упаковки по умолчанию для текущей платформы. Значение Pack должно быть равно 0, 1, 2, 4, 8, 16, 32, 64 или 128:

Поля экземпляра типа выравниваются с помощью следующих правил:

  • Выравнивание типа — это размер его самого большого элемента (1, 2, 4, 8 и т. д.) или указанный размер упаковки в зависимости от того, что меньше.

  • Каждое поле должно соответствовать полям собственного размера (1, 2, 4, 8 и т. д.) или выравниванию типа в зависимости от того, что меньше. Так как выравнивание по умолчанию для типа — это размер самого большого элемента, который больше или равен всем остальным длинам полей, это обычно означает, что поля выравниваются по их размеру. Например, даже если самым большим полем в типе является 64-битовое (8-байтовое) целое число или поле Pack имеет значение 8, Byte поля выравниваются по 1-байтовой границе, Int16 поля — по 2-байтовой границе, а Int32 поля — по 4-байтовой границе.

  • Для соответствия требованиям к выравниванию между полями добавляется заполнение.

Например, рассмотрим следующую структуру, состоящую из двух Byte полей и одного Int32 поля, если она используется с различными Pack значениями для поля.

using System;

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

Важно!

Для успешной компиляции примеров C# необходимо указать параметр компилятора /unsafe .

Если указать размер упаковки по умолчанию, размер структуры составляет 8 байт. Два байта занимают первые два байта памяти, так как байты должны выравнивать по однобайтовой границе. Так как выравнивание по умолчанию для типа составляет 4 байта, то есть размер его самых больших полей , i3есть два байта заполнения, за которым следует целочисленное поле.

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

Если Pack задано значение 2, размер структуры составляет 6 байт. Как и раньше, эти два байта занимают первые два байта памяти. Так как поля теперь выравниваются по 2-байтовой границе, между вторым и целым числом нет заполнения.

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

Если Pack задано значение 4, размер структуры будет таким же, как и в случае по умолчанию, где выравнивание типа определялось размером его самого большого поля , i3т. е. 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

Если Pack задано значение 8, размер структуры будет таким же, как и в случае по умолчанию, так как i3 поле выравнивается по 4-байтовой границе, которая меньше 8-байтовой границы, заданной полем Pack.

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

В другом примере рассмотрим следующую структуру, которая состоит из двух байтовых полей, одного 32-разрядного целочисленного поля со знаком, одного одноэлементного массива байтов и десятичного значения. При размере упаковки по умолчанию размер структуры составляет 28 байт. Два байта занимают первые два байта памяти, затем два байта заполнения, а затем целое число. Далее идет однобайтовый массив, за которым следуют три байта заполнений. Наконец, Decimal поле d5 выравнивается по 4-байтовой границе, так как десятичное значение состоит из четырех Int32 полей, поэтому его выравнивание основано на размере самого большого из полей, а не на размере структуры в Decimal целом.

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

Если Pack задано значение 2, размер структуры составляет 24 байта. По сравнению с выравниванием по умолчанию два байта заполнения между двумя байтами и целым числом были удалены, так как теперь выравнивание типа составляет 4, а не 2. А три байта заполнений после a4 были заменены одним байтом отступа, так как d5 теперь выравнивается на 2-байтовой границе, а не на 4-байтовой границе.

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

Если Pack задано значение 8, размер структуры будет таким же, как и в случае по умолчанию, так как все требования к выравниванию в этой структуре меньше 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

Поле Pack часто используется при экспорте структур во время операций записи на диск и в сеть. Поле также часто используется во время вызовов платформы и операций взаимодействия.

Иногда это поле используется для снижения требований к памяти за счет уменьшения размера упаковки. Однако такое использование требует тщательного рассмотрения фактических аппаратных ограничений и может привести к снижению производительности.

Применяется к