到目前为止,我刚刚理所当然地认为Marshal.SizeOf是计算非托管堆上blittable结构的内存大小的正确方法(这似乎是SO上的共识,几乎是网络上的其他地方).
但在阅读了一些针对Marshal.SizeOf的警告之后(this article之后“但是有问题……”)我试了一下,现在我完全糊涂了:
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错误的大小操作符为非托管结构?
我发现没有什么能用我理解的语言解释这一点.除了“…结构布局是不可发现的……”和“……在大多数情况下……”这种愚蠢的东西.
解决方法:
我认为你仍然没有回答的一个问题是你的特殊情况:
&ps2->x
0x02ca4370 <------
*&ps2->x: 0xabcd 'ꯍ'
&ps2->y
0x02ca4372 <-------
*&ps2->y: 0x1234 'ሴ'
您正在写入(可能)未分配的内存并从中读取.由于您所在的存储区域,因此未检测到.
这将重现预期的行为(至少在我的系统上,YMMV):
TestStruct* ps2 = (TestStruct*)Marshal.AllocHGlobal(marshalSize*10000);
// hmmm, put to 16 bit numbers into only 2 allocated
// bytes, this must surely fail...
for (int i = 0; i < 10000; i++)
{
ps2->x = (char)0xABCD;
ps2->y = (char)0x1234;
ps2++;
}