C#与C++进行字段内存对齐

通过预留字段来补齐内存分配。在实际项目中采用这种方法较多,即保证了长度一致,也为以后的扩展提供了容错的可能性。

csharp 复制代码
unsafe struct StructSequential
        {
            public fixed float x[8];
            public fixed float y[8];
            public fixed float z[8];
            public fixed float time[8];
            public fixed float radius[8];
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
            public float[] reserved;
        }

或者

csharp 复制代码
public unsafe struct RTCPointQuery8
        {
            public fixed float x[8];
            public fixed float y[8];
            public fixed float z[8];
            public fixed float time[8];
            public fixed float radius[8];
        }
Span<byte> stack = stackalloc byte[sizeof(RTCPointQuery8) + 32];
ref var test = ref StackAllocAligned<RTCPointQuery8>(stack, 32);

static unsafe ref T StackAllocAligned<T>(Span<byte> stack, nuint alignment) where T : unmanaged
        {
            return ref Unsafe.AsRef<T>((void*)(((nint)Unsafe.AsPointer(ref MemoryMarshal.GetReference(stack))
                + ((nint)alignment - 1)) & ~(nint)(alignment - 1)));
        }

C#内存对齐有三种方式 ,分别是:LayoutKind.SequentialLayoutKind.ExplicitLayoutKind.Auto

LayoutKind.Sequential

顺序对齐,这是默认的内存对齐方式。会按照定义的struct中最大成员的长度进行内存对齐,使用这种对齐方式容易造成内存空间浪费。这种方式常用于和非托管代码交互的情形,CLR对struct的Layout处理方法与C/C++中默认的处理方式相同,都是按照结构体中占用空间最大的成员进行内存对齐。

csharp 复制代码
[StructLayout(LayoutKind.Sequential)]
        struct StructSequential
        {
            public int A;
            public char B;
        }

        public static void Start()
        {
            StructSequential structSequential = new StructSequential();
            Debug.WriteLine("A的长度为:{0}", Marshal.SizeOf(structSequential.A));
            Debug.WriteLine("B的长度为:{0}", Marshal.SizeOf(structSequential.B));
            Debug.WriteLine("structSequential的长度为:{0}", Marshal.SizeOf(structSequential));
        }
//A的长度为:4
//B的长度为:1
//structSequential的长度为:8

如果我们正在创建一个与非托管代码没有任何交互的struct类型,我们也希望改变C#编译器的这种默认规则,因此,除了Sequential外,还有Explicit和Auto。

除此之外,你还可以通过设置StructLayout.Pack的值来达到内存大小分配一致,但这种方法可能会因硬件性能约束或者其他的问题。

csharp 复制代码
[StructLayout(LayoutKind.Sequential, Pack = 32)]
        unsafe struct StructSequential
        {
            public fixed float x[8];
            public fixed float y[8];
            public fixed float z[8];
            public fixed float time[8];
            public fixed float radius[8];
        }

LayoutKind.Explicit

精确对齐,CLR会按照程序的设置对内存进行精确对齐。传入LayoutKind.Explicit,可以使字段按照我们设定的FieldOffset设置字段排序方式,CLR不对结构体进行内存对齐;但是,这种方式也是有缺陷的,如果设置错误,后果将比较严重。

csharp 复制代码
[StructLayout(LayoutKind.Explicit)]
        struct StructSequential
        {
            [FieldOffset(0)]
            public int A;
            [FieldOffset(0)]
            public char B;
        }

        public static void Start()
        {
            StructSequential structSequential = new StructSequential();
            Debug.WriteLine("A的长度为:{0}", Marshal.SizeOf(structSequential.A));
            Debug.WriteLine("B的长度为:{0}", Marshal.SizeOf(structSequential.B));
            Debug.WriteLine("structSequential的长度为:{0}", Marshal.SizeOf(structSequential));
            structSequential.A = 0;
            structSequential.B = '0';
            Debug.WriteLine("structSequential.A: {0}", structSequential.A);
            Debug.WriteLine("structSequential.B: {0}", structSequential.B);
        }
//A的长度为:4
//B的长度为:1
//structSequential的长度为:4
//structSequential.A: 48
//structSequential.B: 0

FieldOffset(0)表示内存不会有任何偏移,这种情形下,同一字节中存储了两条数据,当其中一条改变时,另一条也随之而改变。

LayoutKind.Auto

自动对齐。CLR会调整struct中成员的顺序来自动对齐,使实例仅可能占用更少的内存,同时会进行一定字节的内存对齐。 按照自己选择的最优方式对实例中的字段进行排列。

csharp 复制代码
[StructLayout(LayoutKind.Auto)]
        struct StructSequential
        {
            public int A;
            
            public char B;
        }

        public static void Start()
        {
            StructSequential structSequential = new StructSequential();
            Debug.WriteLine("A的长度为:{0}", Marshal.SizeOf(structSequential.A));
            Debug.WriteLine("B的长度为:{0}", Marshal.SizeOf(structSequential.B));
            //Debug.WriteLine("structSequential的长度为:{0}", Marshal.SizeOf(structSequential));
            unsafe
            {
                Debug.WriteLine("structSequential的长度为:{0}", sizeof(StructSequential));
            }
            structSequential.A = 0;
            structSequential.B = 'a';
            Debug.WriteLine("structSequential.A: {0}", structSequential.A);
            Debug.WriteLine("structSequential.B: {0}", structSequential.B);
        } 
相关推荐
天空之外1364 小时前
Docker安装MySQL 8.4 LTS、Docker安装Redis 7.4.x、Docker安装MinIO
java·mysql·docker
乱七八糟的屋子5 小时前
MTL4 (Matrix Template Library) 超详细实战教程|C++高性能稠密/稀疏矩阵计算
c++·矩阵·稀疏矩阵·mtl4·matrixtemplate·c++线性代数·高性能数值计算
fīɡЙtīиɡ ℡5 小时前
深入学习JAVA并发编程(上)
java·开发语言·学习
XS0301065 小时前
Spring AI:两种内置 Advisor 快速实现 RAG
java·人工智能·spring
mifengxing5 小时前
LeetCode 238 除自身以外数组的乘积|无除法O(n)时间+O(1)空间双解法
java·算法·leetcode
猿长大人5 小时前
C# | 函数式编程入门
开发语言·c#·.net
依然鸣6 小时前
PTA团体程序设计天梯赛L2真题讲解L2-045-048
数据结构·c++·经验分享·学习·算法·pat考试·pat
有点。6 小时前
C++深度优先搜索(二)
开发语言·c++·深度优先
小林敲代码77886 小时前
Spring Boot 3 升级踩坑:aj-captcha 行为验证码底图加载失效
java·spring boot·后端
带刺的坐椅6 小时前
Solon I18n:三种解析器,零样板代码
java·国际化·i18n·solon·多语言