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


    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)*/
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    3 C# 语言写法

    
    using System.Runtime.InteropServices;
    
    [StructLayout(LayoutKind.Sequential, Pack = 4)]
    struct Test_Struct
    {
    	byte a1;
    	byte a2;
    	int b1;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    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 。
    
    • 1

    C# 代码:

    using System;
    
    struct ExampleStruct
    {
       public byte b1;
       public byte b2;
       public int i3;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    重要

    若要成功编译 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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    内存对齐 - 示例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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    内存对齐 - 示例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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    内存对齐 - 示例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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    内存对齐 - 示例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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    内存对齐 - 示例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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    内存对齐 - 说明

    在 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
  • 相关阅读:
    《Effective C++》条款21
    Apple Silicon配置二进制环境(一)
    图像处理领域之►边缘检测大合集◄【应该是全网仅有的了吧】
    docker安装&部署vue
    ArcGIS综合制图教程,简单上手!
    Java日志框架Log4j 2详解
    SpringBoot默认开启AOP,采用Cglib代理方式?(Spring AOP快速入门)
    《C++ Primer Plus》第九章:内存模型和名称空间(1)
    ping与Traceroute是如何工作的
    02 | 如何进行code diff
  • 原文地址:https://blog.csdn.net/weixin_38531633/article/details/127802364