运行此代码时:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace StructLayoutTest
{
class Program
{
unsafe static void Main()
{
Console.WriteLine(IntPtr.Size);
Console.WriteLine();
Sequential s = new Sequential();
s.A = 2;
s.B = 3;
s.Bool = true;
s.Long = 6;
s.C.Int32a = 4;
s.C.Int32b = 5;
int* ptr = (int*)&s;
Console.WriteLine(ptr[0]);
Console.WriteLine(ptr[1]);
Console.WriteLine(ptr[2]);
Console.WriteLine(ptr[3]);
Console.WriteLine(ptr[4]);
Console.WriteLine(ptr[5]);
Console.WriteLine(ptr[6]);
Console.WriteLine(ptr[7]); //NB!
Console.WriteLine("Press any key");
Console.ReadKey();
}
[StructLayout(LayoutKind.Explicit)]
struct Explicit
{
[FieldOffset(0)]
public int Int32a;
[FieldOffset(4)]
public int Int32b;
}
[StructLayout(LayoutKind.Sequential, Pack = 4)]
struct Sequential
{
public int A;
public int B;
public bool Bool;
public long Long;
public Explicit C;
}
}
}
我希望在 x86 和 x64 上都有这个输出:
4 或 8(取决于 x86 或 x64)
2
3
1
6
0
4
5
垃圾
我在 x86 上得到的是:
4
6
0
2
3
1
4
5
垃圾
我在 x64 上得到的是:
8
6
0
2
3
1
0
4
5
更多:
- 当我删除 LayoutKind.Explicit 和 FieldOffset 属性时,问题就消失了。
- 当我删除 Bool 字段时,问题就消失了。
- 当我删除长字段时,问题就消失了。
- 请注意,在 x64 上似乎 Pack=4 属性参数也被忽略了?
这适用于 .Net3.5 和 .Net4.0
我的问题:我错过了什么?或者这是一个错误?
我发现了一个类似的问题:
如果结构包含 DateTime 字段,为什么 LayoutKind.Sequential 的工作方式会有所不同?
但在我的情况下,即使子结构的属性发生变化,布局也会发生变化,而数据类型没有任何变化。所以它看起来不像是优化。除此之外,我想指出,另一个问题仍未得到解答。
在另一个问题中,他们提到使用编组时尊重布局。我自己还没有测试过,但我想知道为什么布局不尊重不安全代码,因为所有相关属性似乎都已经到位?文档是否在某处提到除非完成编组,否则这些属性将被忽略?为什么?
考虑到这一点,我什至可以期望 LayoutKind.Explicit 能够可靠地处理不安全的代码吗?
此外,文档提到了保持结构与预期布局的动机:
为了减少与 Auto 值相关的布局相关问题,C#、Visual Basic 和 C++ 编译器为值类型指定了顺序布局。
但是这个动机显然不适用于不安全的代码?