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 字节边界上对齐。
在字段之间添加填充以满足对齐要求。
例如,考虑以下结构,当它与字段的各种值Pack一起使用时,它由两Byte个字段和一个Int32字段组成。
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 磁盘和网络写入操作期间导出结构时,经常使用 字段。 在平台调用和互操作操作期间,也经常使用 字段。
有时, 字段用于通过生成更紧密的打包大小来降低内存需求。 但是,此用法需要仔细考虑实际硬件约束,实际上可能会降低性能。
适用于
反馈
https://aka.ms/ContentUserFeedback。
即将发布:在整个 2024 年,我们将逐步淘汰作为内容反馈机制的“GitHub 问题”,并将其取代为新的反馈系统。 有关详细信息,请参阅:提交和查看相关反馈