C#对布尔字段和固定数组的StructLayout感到困惑

本文关键字:StructLayout 数组 布尔 字段 | 更新日期: 2023-09-27 18:21:08

我正在研究基于内存映射文件与非.Net应用程序的IPC数据交换。因此,我试图了解C#如何组织物理数据布局。所以我从不安全的结构开始,因为从我之前读到的内容来看,这似乎是一种正确的技术。当使用bool类型值的不同数据布局时,我对这段代码和结果感到困惑:

using System;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace UnsafeBoolArrayStruct
{
    [StructLayout(LayoutKind.Sequential, Pack = 2)]
    public unsafe struct StructOfBool
    {
        public bool Bool_00;
        public fixed bool Bool_01[1];
        public bool Bool_02;
        public fixed bool Bool_03[2];
        public fixed bool Bool_05[3];
        public fixed bool Bool_08[4];
        public fixed bool Bool_12[5];
        public fixed bool Bool_17[7];
        public fixed bool Bool_24[8];
        public fixed bool Bool_32[9];
        public fixed bool Bool_41[2];
        public bool Bool_43;
    }
    class Program
    {
        static void Main(string[] args)
        {
            StructOfBool dummy = new StructOfBool();
            int _Size = Marshal.SizeOf(typeof(StructOfBool));
            int _Pack = typeof(StructOfBool).StructLayoutAttribute.Pack;
            Console.WriteLine("{0} -> Pack: {1}, Size: {2}", typeof(StructOfBool).Name, _Pack, _Size);
            foreach (FieldInfo fi in typeof(StructOfBool).GetFields())
            {
                Type _Type;
                int _ArrayLength = 0;
                _Size = Marshal.SizeOf(fi.FieldType);
                var _Offset = Marshal.OffsetOf(typeof(StructOfBool), fi.Name);
                // Check for arrays
                var _Attribute = fi.GetCustomAttributes(typeof(FixedBufferAttribute), false);
                if (_Attribute.Length > 0)
                {   // Array
                    _Type = ((FixedBufferAttribute)_Attribute[0]).ElementType;
                    _ArrayLength = ((FixedBufferAttribute)_Attribute[0]).Length;
                }
                else
                    // Singular field
                    _Type = fi.FieldType;
                // Process found data
                Console.WriteLine("{0} -> Type: {1}, ArrayLength: {2}, Offset: {3}, Size: {4}", fi.Name, Type.GetTypeCode(_Type), _ArrayLength, _Offset, _Size);
            }
            Console.ReadKey();
        }
    }
}

结果显示如下:

StructOfPool->包:2,尺寸:64

Bool_00->类型:布尔,数组长度:0,偏移:0,大小:4

Bool_01->类型:布尔,数组长度:1,偏移:4,大小:4

Bool_02->类型:布尔,数组长度:0,偏移:8,大小:4

Bool_03->类型:布尔,数组长度:2,偏移:12,大小:4

Bool_05->类型:布尔,数组长度:3,偏移:16,大小:4

Bool_08->类型:布尔,数组长度:4,偏移:20,大小:4

Bool_12->类型:布尔,数组长度:5,偏移:24,大小:5

Bool_17->类型:布尔,数组长度:7,偏移:30,大小:7

Bool_24->类型:布尔型,阵列长度:8,偏移:38,大小:8

Bool_32->类型:布尔,数组长度:9,偏移:46,大小:9

Bool_41->类型:布尔,数组长度:2,偏移:56,大小:4

Bool_43->类型:布尔,数组长度:0,偏移:60,大小:4

与数组相比,打包的处理方式似乎有所不同,至少在奇异字段上是这样。为什么具有一个布尔值的Bool00占用4个字节,具有两个布尔值(bool value)的Bool03也占用4字节,而具有五个值的Bool12只占用5个字节??

有人知道吗,为什么?

C#对布尔字段和固定数组的StructLayout感到困惑

而CCD_ 1的5个值只有5个字节??

公平地说,您的结果显示Bool12实际上在结构中占用了六个字节。

Marshal.SizeOf()方法不会告诉您任何关于数据的托管表示的信息,包括结构中字段的大小。对于需要知道给定类型的非托管版本在空间方面需要什么的代码,需要该方法。

我根本不知道托管类型的所有布局规则。但是,您的观察结果可以通过考虑数组的实际大小不必是数组元素的最小大小的倍数,甚至是数组本身的倍数来解释。因此,你得到的boolbool[]字段是四个字节,因为它们不能再小了,但一旦超过这个大小,pack(对齐)值就会接管,使字段的偏移量相等。

您使用Marshal.SizeOf()获得的这些字段的报告长度,正如我上面提到的,它与托管存储根本无关。它告诉您非托管代码中的目标缓冲区需要多少字节才能存储该字段中的信息。

即使它是相关的(巧合的是,在某种程度上它是…,只是因为CLR端的工作方式与非托管端非常相似),这也不会改变结构中的字段需要对齐到两个字节的边界这一事实,因此后续字段的偏移量并不总是与它们各自的前一个字段的报告长度相匹配。


相关链接:
如何检查我的结构所消耗的字节数
sizeof vs Marshal.sizeof
计算结构的大小
有什么区别?sizeof和Marshal.sizeof