C#.NET对布尔字段和固定数组的结构布局感到困惑
我正在研究基于内存映射文件与非.Net应用程序的IPC数据交换。因此,我试图了解C#如何为此组织物理数据布局。因此,我从不安全结构开始,从我之前阅读的内容来看,这似乎是正确的技术。在为布尔类型值使用不同的数据布局时,我对以下代码和结果感到困惑:C#.NET对布尔字段和固定数组的结构布局感到困惑,c#,unsafe,C#,Unsafe,我正在研究基于内存映射文件与非.Net应用程序的IPC数据交换。因此,我试图了解C#如何为此组织物理数据布局。因此,我从不安全结构开始,从我之前阅读的内容来看,这似乎是正确的技术。在为布尔类型值使用不同的数据布局时,我对以下代码和结果感到困惑: using System; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace
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();
}
}
}
结果如下:
StructOfBool->Pack:2,大小:64
Bool_00->类型:Boolean,数组长度:0,偏移量:0,大小:4
Bool_01->类型:Boolean,数组长度:1,偏移量:4,大小:4
Bool_02->类型:Boolean,数组长度:0,偏移量:8,大小:4
Bool_03->类型:Boolean,数组长度:2,偏移量:12,大小:4
Bool_05->类型:Boolean,排列长度:3,偏移量:16,大小:4
Bool_08->类型:Boolean,数组长度:4,偏移量:20,大小:4
Bool_12->类型:Boolean,数组长度:5,偏移量:24,大小:5
Bool_17->类型:Boolean,数组长度:7,偏移量:30,大小:7
Bool_24->类型:Boolean,数组长度:8,偏移量:38,大小:8
Bool_32->类型:Boolean,数组长度:9,偏移量:46,大小:9
Bool_41->类型:Boolean,数组长度:2,偏移量:56,大小:4
Bool_43->类型:Boolean,数组长度:0,偏移量:60,大小:4
看起来,至少在奇异场上,与数组相比,填充的处理方式有所不同。为什么一个bool值的Bool00需要4个字节,两个bool值的Bool03也需要4个字节,而五个值的Bool12只需要5个字节
有人知道为什么吗
但是Bool12
有五个值只有5个字节
平心而论,您的结果显示,Bool12
实际上占用结构中的六个字节
Marshal.SizeOf()
方法不会告诉您有关数据的托管表示的任何信息,包括结构中字段的大小。对于需要知道给定类型的非托管版本在空间方面需要什么的代码,需要使用该方法
我不知道托管类型的所有布局规则。但是,可以通过考虑数组的实际大小不必是数组元素的最小大小的倍数,甚至不必是数组本身的倍数来解释您的观察结果。因此,您会得到四个字节的bool
和bool[]
字段,因为它们不能再小,但一旦超过该大小,pack(alignment)值就会接管,将字段置于偶数值偏移处
通过使用Marshal.SizeOf()
获得的这些字段的报告长度,正如我前面提到的,这些字段与托管存储完全无关。它告诉您非托管代码中的目标缓冲区需要多少字节才能在该字段中存储信息
即使它是相关的(巧合的是,在某种程度上它是…因为CLR端的工作方式与非托管端非常相似),这也不会改变结构中的字段需要与两个字节边界对齐的事实,因此,后续字段的偏移量并不总是与其各自的前一字段的报告长度相匹配
相关链接:
但是
Bool12
有五个值只有5个字节
平心而论,您的结果显示,Bool12
实际上占用结构中的六个字节
Marshal.SizeOf()
方法不会告诉您有关数据的托管表示的任何信息,包括结构中字段的大小。对于需要知道给定类型的非托管版本在空间方面需要什么的代码,需要使用该方法
我不知道托管类型的所有布局规则。但是,可以通过考虑数组的实际大小不必是数组元素的最小大小的倍数,甚至不必是数组本身的倍数来解释您的观察结果。因此,您会得到四个字节的bool
和bool[]
字段,因为它们不能再小,但一旦超过该大小,pack(alignment)值就会接管,将字段置于偶数值偏移处
通过使用Marshal.SizeOf()
获得的这些字段的报告长度,正如我前面提到的,这些字段与托管存储完全无关。它告诉您非托管代码中的目标缓冲区需要多少字节才能在该字段中存储信息
即使它是相关的(巧合的是,在某种程度上它是…因为CLR端的工作方式与非托管端非常相似),这也不会改变结构中的字段需要与两个字节边界对齐的事实,因此,后续字段的偏移量并不总是与其各自的前一字段的报告长度相匹配
相关链接:
您使用哪种体系结构?这很可能是一个对齐问题。我认为有一个指向实际数据位置的指针。指针可以是指向实际数据的不同大小的地址指针。结构包含一个类型作为第一个位置,该位置将指示变量类型以及结构是否包含实际数据或指向数据的指针。另外请注意,偏移量是向上舍入到偶数(对齐)的大小。您不可能选择更差的互操作类型,bool类型的标准化非常差。它是C++中的1字节,C中的4字节,COM中的2字节,CLR中的1字节。用它制作一个固定的缓冲区突然改变了这一点,现在它作为一个字节进行互操作。使用Pack=2也不太可能,没有人使用它。通过使用byte而不是bool获得成功。并将普通字节[]与[Marshallas(UnmanagedType.ByValArray),SizeConst=xx]一起使用。您可以添加一个属性来将字节转换为布尔值。您使用哪种体系结构?很可能这是一个对齐问题。我认为有一个指向re的指针