控制类或结构的数据字段在内存中的对齐方式

1 概述

由于内存的读取时间远远小于CPU的存储速度,这里用设定数据结构的对齐系数,即牺牲空间来换取时间的思想来提高CPU的存储效率。

2 C/C++ 语言写法

#pragma pack(push,4) /*指定按4字节对齐,等价于#pragma pack(push,4)*/

struct Test_Struct
{
	char a1; //a1的自身对齐值为1, 小于对齐的四个字节,放在A空间
	short a2; //a2的自身对齐值为2,1+2 小于对齐的四个字节,放在A空间
	int a3; //a3的自身对齐值为4,4长度不足以容纳a1,a2,a3一起,所以a3额外进行4字节对齐
		//这里一共使用8个字节长度
}

#pragma pack(pop) /*取消指定对齐,恢复缺省对齐,等价于#pragma pack(pop)*/

3 C# 语言写法


using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential, Pack = 4)]
struct Test_Struct
{
	byte a1;
	byte a2;
	int b1;
}

4 MS文档参考

文档参考:
https://learn.microsoft.com/zh-cn/dotnet/api/system.runtime.interopservices.structlayoutattribute.pack?view=net-7.0

5 C#内存对齐参考

5.1 StructLayoutAttribute.Pack 字段

命名空间:

System.Runtime.InteropServices

程序集:

netstandard.dll

字段值

Int32

控制类或结构的数据字段在内存中的对齐方式。

public int Pack;

5.2 注解

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 。

C# 代码:

using System;

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

重要

若要成功编译 c # 示例,必须指定 /unsafe 编译器开关。

6 C# 内存对齐 - 示例1

内存对齐 - 示例1 - pack=0

如果指定默认的打包大小,则结构的大小为8个字节。 这两个字节占用前两个字节的内存,因为字节必须在一个字节边界上对齐。 由于该类型的默认对齐方式为4字节(这是其最大字段的大小),因此 i3 有两个填充字节,后跟整数字段。

C#

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

内存对齐 - 示例1 - pack=2

如果 Pack 设置为2,则结构的大小为6个字节。 与之前一样,两个字节占用前两个字节的内存。 由于字段现在按2字节边界对齐,因此,在第二个字节和整数之间没有任何空白。

C#

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

内存对齐 - 示例1 - pack=4

如果 Pack 设置为4,则结构的大小与默认大小写相同,其中,类型的对齐方式由其最大字段的大小定义,即 i3 4。

C#

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

内存对齐 - 示例1 - pack=8

如果 Pack 设置为8,则结构的大小与默认情况下的大小相同,因为 i3 字段在4字节边界上对齐,这小于 Pack 字段指定的8字节边界。

C#

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

7 C# 内存对齐 - 示例2

内存对齐 - 示例2

若要获得另一个示例,请考虑以下结构,其中包含两个字节字段、1 32 位带符号整数字段、单元素字节数组和十进制值。 对于默认的封装大小,结构的大小为28个字节。 这两个字节占用前两个字节的内存,后跟两个填充字节,后跟整数。 接下来是单字节数组,后跟三个填充字节。 最后, Decimal 由于一个十进制值由四个字段组成,因此该字段 d5 在4字节边界上对齐, Int32 因此,其对齐方式基于其字段的最大大小,而不是 Decimal 整个结构的大小。

C#

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

内存对齐 - 示例2 - pack=2

如果 Pack 设置为2,则结构的大小为24个字节。 与默认对齐方式相比,两个字节和整数之间的两个填充字节已被删除,因为该类型的对齐方式现在为4而不是2。 并将填充后的三个字节 a4 替换为一个填充字节,因为 d5 现在在2字节边界上对齐,而不是在4字节边界上对齐。

C#

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

内存对齐 - 示例2 - pack=8

如果 Pack 设置为8,则结构的大小与默认情况相同,因为此结构中的所有对齐要求都小于8。

C#

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 磁盘和网络写入操作过程中导出结构时,通常会使用该字段。 在平台调用和互操作操作期间,也经常使用此字段。

偶尔,该字段用于通过生成更严格的封装大小来减少内存需求。 但是,这种用法需要仔细考虑实际的硬件限制,并可能会降低性能。

C#内存对齐适用版本

产品 版本

.NETCore 1.0, Core 1.1, Core 2.0, Core 2.1, Core 2.2, Core 3.0, Core 3.1, 5, 6, 7
.NET Framework1.1, 2.0, 3.0, 3.5, 4.0, 4.5, 4.5.1, 4.5.2, 4.6, 4.6.1, 4.6.2, 4.7, 4.7.1, 4.7.2, 4.8
.NET Standard1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 2.0, 2.1
UWP10.0
Xamarin.iOS10.8
Xamarin.Mac3.0
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值