StructLayoutAttribute.Pack フィールド

定義

メモリ内のクラスまたは構造体のデータ フィールドの配置を制御します。

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

フィールド値

注釈

Pack フィールドは、メモリ内の型のフィールドの配置を制御します。 LayoutKind.Sequential プロパティに影響します。 この値は、現在のプラットフォームの既定のパッキング サイズを示します。 Packの値は、0、1、2、4、8、16、32、64、または 128 である必要があります。 既定値は 0 です。

型インスタンスのフィールドは、次の規則を使用して配置されます。

  • 型の配置は、最大要素のサイズ (1、2、4、8 バイトなど) または指定されたパッキング サイズのいずれか小さい方です。
  • 各フィールドは、独自のサイズのフィールドまたは型の配置のいずれか小さい方に揃える必要があります。 型の既定の配置は、その最大要素のサイズであり、他のすべてのフィールド長以上であるため、通常はフィールドがサイズに合わせて配置されることを意味します。 たとえば、型の最大フィールドが 64 ビット (8 バイト) の整数であるか、Pack フィールドが 8 に設定されている場合でも、 Byte フィールドは 1 バイト境界に配置され、 Int16 フィールドは 2 バイト境界に配置され、 Int32 フィールドは 4 バイト境界に配置されます。
  • 配置要件を満たすために、フィールド間にパディングが追加されます。

たとえば、2 つの Byte フィールドと 1 つの Int32 フィールドで構成される次の構造を、 Pack フィールドのさまざまな値と共に使用するとします。

using System;

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

Important

C# の例を正常にコンパイルするには、 /unsafe コンパイラ スイッチを指定する必要があります。

既定のパッキング サイズを指定した場合、構造体のサイズは 8 バイトです。 バイトは 1 バイト境界に配置する必要があるため、2 バイトは最初の 2 バイトのメモリを占有します。 型の既定の配置は 4 バイトであり、最大のフィールドのサイズである i3 であるため、埋め込みの 2 バイトの後に整数フィールドが続きます。

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

Packが 2 に設定されている場合、構造体のサイズは 6 バイトです。 前と同様に、2 バイトは最初の 2 バイトのメモリを占有します。 フィールドは 2 バイトの境界に揃えられるようになったため、2 番目のバイトと整数の間にパディングはありません。

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

Packが 4 に設定されている場合、構造体のサイズは既定の場合と同じになります。この場合、型の配置は最大フィールドのサイズ (i3、 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

Packが 8 に設定されている場合、i3 フィールドは 4 バイト境界 (Pack フィールドで指定された 8 バイト境界より小さい) に配置されるため、構造体のサイズは既定の場合と同じです。

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

別の例として、2 つのバイト フィールド、1 つの 32 ビット符号付き整数フィールド、1 つの単一要素バイト配列、および 10 進値で構成される次の構造体を考えてみましょう。 既定のパッキング サイズでは、構造体のサイズは .NET Framework では 28 バイト、.NET 5 以降では 32 バイトです。 2 バイトは最初の 2 バイトのメモリを占有し、その後に 2 バイトのパディングが続き、その後に整数が続きます。 次に、1 バイトの配列の後に 3 バイトのパディングが続きます。 10 進値は複数のフィールドで構成されるため、アラインメントは、 Decimal 構造体全体のサイズではなく、そのフィールドの最大サイズに基づきます。 .NET 5 以降のバージョンでは、Decimal 構造体は 2 つの Int32 フィールドと 1 つの 8 バイト フィールドで構成されるため、Decimal フィールド d5 は 8 バイト境界に配置されます。 .NET Framework では、Decimal 構造体は 4 つの Int32 フィールドで構成されるため、Decimal フィールド d5 は 4 バイト境界に配置されます。

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

Packが 2 に設定されている場合、構造体のサイズは 24 バイトです。 既定の配置と比較して、型の配置が 2 ではなく 4 であるため、2 バイトと整数の間の 2 バイトのパディングが削除されました。 また、 a4 後の 3 バイトのパディングは、4 バイトの境界ではなく 2 バイトの境界に配置されるようになった d5 ため、1 バイトのパディングに置き換えられました。

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

Packが 16 に設定されている場合、構造体のサイズは既定の場合と同じです。これは、この構造体のすべての配置要件が 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

Pack フィールドは、ディスクおよびネットワーク書き込み操作中に構造体がエクスポートされるときによく使用されます。 このフィールドは、プラットフォーム呼び出しおよび相互運用操作中にも頻繁に使用されます。

場合によっては、このフィールドを使用して、より厳密なパッキング サイズを生成することでメモリ要件を減らすことができます。 ただし、この使用には実際のハードウェア制約を慎重に考慮する必要があり、実際にはパフォーマンスが低下する可能性があります。

適用対象