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 バイト境界に配置されInt32Int16フィールドは 2 バイト境界に揃え、フィールドは 4 バイト境界に揃えられます。
  • 配置要件を満たすために、フィールド間にパディングが追加されます。

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

using System;

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

重要

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

が 2 に設定されている場合 Pack 、構造体のサイズは 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

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

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

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 つのフィールドと 1 つの Int32 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

が 2 に設定されている場合 Pack 、構造体のサイズは 24 バイトです。 既定のアラインメントと比較して、2 バイトと整数の間の 2 バイトのパディングは削除されました。これは、型のアラインメントが 2 ではなく 4 であるためです。 以降の 3 バイトのパディング a4 は、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

が 16 に設定されている場合 Pack 、構造体のサイズは、この構造体のすべての配置要件が 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 、ディスクおよびネットワーク書き込み操作中に構造体がエクスポートされるときによく使用されます。 フィールドは、プラットフォーム呼び出しおよび相互運用操作中にも頻繁に使用されます。

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

適用対象