Share via


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 位元組界限。
  • 欄位之間會新增填補,以滿足對齊需求。

例如,假設下列結構是由兩 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 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 位元組界限,所以第二個字節與整數之間沒有邊框間距。

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

若要採用另一個範例,請考慮下列結構,其中包含兩個字節位元段、一個32位帶正負號的整數位段、一個單一元素位元組陣列和十進位值。 使用預設封裝大小時,結構的大小為 28 位元組,.NET Framework 為 32 個字節,而 .NET 5+ 中為 32 個字節。 兩個字節佔用前兩個字節的記憶體,後面接著兩個字節的填補,後面接著整數。 接下來是一位元組陣列,後面接著三個字節的填補。 因為十進位值是由數個字段所組成,所以對齊是根據其最大字段,而不是整個結構的大小 Decimal 。 在 .NET 5 和更新版本中,結構 Decimal 包含兩 Int32 個字段和一個8位元組欄位,因此 Decimal 欄位元組5 會對齊8位元組界限。 在 .NET Framework 中,結構Decimal包含四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 個字節。 相較於預設對齊方式,已移除兩個字節與整數之間的兩個字節填補,因為類型的對齊現在是 4 而不是 2。 之後的三個字節填補 a4 已由一個字節填補取代,因為 d5 現在會對齊 2 位元組界限,而不是 4 位元組界限。

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 磁碟和網路寫入作業期間導出結構時,經常會使用欄位。 欄位也經常在平臺叫用和 Interop 作業期間使用。

有時候,欄位會藉由產生更嚴格的封裝大小來減少記憶體需求。 不過,此使用方式需要仔細考慮實際的硬體條件約束,而且可能會實際降低效能。

適用於