到目前为止,我一直认为 Marshal.SizeOf 是计算非托管堆上 blittable 结构的内存大小的正确方法(这似乎是 SO 以及网络上几乎所有其他地方的共识)。
但在阅读了一些针对 Marshal.SizeOf 的警告之后(本文 https://www.codeproject.com/Articles/32125/Unmanaged-Arrays-in-C-No-Problem在“但是有一个问题......”之后)我尝试了一下,现在我完全困惑了:
public struct TestStruct
{
public char x;
public char y;
}
class Program
{
public static unsafe void Main(string[] args)
{
TestStruct s;
s.x = (char)0xABCD;
s.y = (char)0x1234;
// this results in size 4 (two Unicode characters)
Console.WriteLine(sizeof(TestStruct));
TestStruct* ps = &s;
// shows how the struct is seen from the managed side... okay!
Console.WriteLine((int)s.x);
Console.WriteLine((int)s.y);
// shows the same as before (meaning that -> is based on
// the same memory layout as in the managed case?)... okay!
Console.WriteLine((int)ps->x);
Console.WriteLine((int)ps->y);
// let's try the same on the unmanaged heap
int marshalSize = Marshal.SizeOf(typeof(TestStruct));
// this results in size 2 (two single byte characters)
Console.WriteLine(marshalSize);
TestStruct* ps2 = (TestStruct*)Marshal.AllocHGlobal(marshalSize);
// hmmm, put to 16 bit numbers into only 2 allocated
// bytes, this must surely fail...
ps2->x = (char)0xABCD;
ps2->y = (char)0x1234;
// huh??? same result as before, storing two 16bit values in
// only two bytes??? next will be a perpetuum mobile...
// at least I'd expect an access violation
Console.WriteLine((int)ps2->x);
Console.WriteLine((int)ps2->y);
Console.Write("Press any key to continue . . . ");
Console.ReadKey(true);
}
}
这里出了什么问题?字段解引用运算符“->”采用什么内存布局? “->”是否是用于寻址非托管结构的正确运算符?或者 Marshal.SizeOf 对于非托管结构来说是错误的大小运算符?
我没有找到任何可以用我理解的语言来解释这一点的东西。除了“……结构布局是不可发现的……”和“……在大多数情况下……”之类的软弱无力的东西。
区别在于:sizeof运算符采用类型名称并告诉您需要为该结构的实例分配多少字节的托管内存。这不一定是堆栈内存;当结构是数组元素、类的字段等时,它们会从堆中分配。相比之下,Marshal.SizeOf采用类型对象或类型实例,并告诉您需要分配多少字节的非托管内存。由于多种原因,这些可能会有所不同。类型的名称给了你一个线索:Marshal.SizeOf旨在将结构封送到非托管内存时使用。
两者之间的另一个区别是sizeof运算符只能采用非托管类型的名称;即结构体类型,其字段仅为整型、布尔值、指针等。 (有关准确定义,请参阅规范。)Marshal.SizeOf相比之下,可以采用任何类或结构类型。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)