StructLayoutAttribute.Pack 欄位
定義
重要
部分資訊涉及發行前產品,在發行之前可能會有大幅修改。 Microsoft 對此處提供的資訊,不做任何明確或隱含的瑕疵擔保。
控制記憶體中類別或結構之資料欄位的對齊。
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 位元組界限,這小於 Pack 欄位所指定的 8 位元組界限。
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 磁片和網路寫入作業期間匯出結構時,通常會使用 欄位。 欄位在平臺叫用和 Interop 作業期間也經常使用。
偶爾會使用 欄位來減少記憶體需求,方法是產生更緊密的封裝大小。 不過,此使用方式需要仔細考慮實際的硬體條件約束,而且實際上可能會降低效能。
適用於
意見反應
https://aka.ms/ContentUserFeedback。
即將登場:在 2024 年,我們將逐步淘汰 GitHub 問題作為內容的意見反應機制,並將它取代為新的意見反應系統。 如需詳細資訊,請參閱:提交並檢視相關的意見反應