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

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
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值