C# 使用非对齐数组封送结构

C# 使用非对齐数组封送结构,c#,arrays,marshalling,memory-alignment,C#,Arrays,Marshalling,Memory Alignment,我在尝试封送此结构时遇到异常 [StructLayout(LayoutKind.Explicit, Pack = 1)] public struct Data { [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4, ArraySubType = UnmanagedType.U1)] [FieldOffset(0x1)] public byte[] a2; } 上面说 无法从程序集“WTF,版本=1.0.0.0,区域性=

我在尝试封送此结构时遇到异常

[StructLayout(LayoutKind.Explicit, Pack = 1)]
public struct Data
{
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4, ArraySubType = UnmanagedType.U1)]
    [FieldOffset(0x1)]
    public byte[] a2;
}
上面说 无法从程序集“WTF,版本=1.0.0.0,区域性=中性,PublicKeyToken=null”加载类型“WTF.Data”,因为它包含偏移量1处的对象字段,该字段未正确对齐或与非对象字段重叠

当我将偏移量1更改为0或4时,一切正常。 我做错了什么


谢谢,

[StructLayout]会影响结构的托管和封送布局。NET中有点怪,但创建blittable结构是互操作的一大胜利,CLR不能忽视托管代码总是在完全非托管的操作系统上运行这一事实。不必创建结构的副本,只需能够传递指向托管版本的指针,这是一个非常重要的性能优势

您的[FieldOffset]值违反了.NET内存模型的有力保证,对象引用分配始终是原子的。一个昂贵的词,意味着另一个线程永远无法观察到仅部分更新的无效对象引用。原子性要求正确对齐,32位模式为4的倍数,64位模式为8的倍数。如果它们没有对齐,那么处理器可能需要执行多个内存总线周期来将字节粘合在一起。这很糟糕,当另一个线程也在更新变量时,它会导致撕裂。从旧值获取部分指针值,从新值获取部分指针值。剩下的是一个损坏的指针,它使垃圾收集器崩溃。非常糟糕


从C#的高级观点来看,模糊的东西,但是提供基本的执行保证是非常重要的。您不能将其偏移到1,只要使用LayoutKind.Explicit,就没有解决方法。所以不要使用它。

请看Hans Passant的答案第一对齐数据是一件好事,CLR强制使用它是有原因的。不过,如果出于某种原因确实需要或想要:

[StructLayout( LayoutKind.Sequential, Pack = 1 )]
public struct Data
{            
    public byte Dummy;

    [MarshalAs( UnmanagedType.ByValArray, SizeConst = 4,
                ArraySubType = UnmanagedType.U1 )]
    public byte[] a2;
}
也可以使用
不安全的
代码:

[StructLayout( LayoutKind.Explicit, Pack = 1 )]
public unsafe struct Data
{
    [FieldOffset( 1 )]
    public fixed byte a2[4];
}
但同样,这可能不是一个好主意


编辑:第三个选项是简单地将数组设置为5字节长,并将其设置为偏移量0,然后忽略第一个字节。这似乎更安全。

为什么要尝试将其设置为0x1?这就是一个例子。我需要读取一个结构,其中字节数组不与4Inopp对齐,C++直接从硬件转发数据结构需要排列错误的数组,但是在浏览时,很难相信大多数C语言开发者需要它。我注意到,如果我们在开始时添加一个虚拟字节,使用<代码> [StuttDebug ]。(LayoutKind.Sequential,Pack=1)]
,似乎有可能使其未对齐。
Marshal.SizeOf=5
Marshal.OffsetOf(a2)=1
。我想这不是一件好事吗?@Chris-也许这是一个可以接受的解决方案。真的不知道,他没有解释为什么他想使用LayoutKind.Explicit。你应该把它作为一个答案发布,我只是关注“为什么它不起作用”部分。你对
不安全的
替代方案有什么看法?我认为这是一种特殊情况,数组实际上不是一个引用类型/
系统。数组
?@Chris-回到基本点,我认为OP应该停止假装计算机开始计数是1而不是0。他的声明没有任何错误恩斯,他得到了“你做错了”的例外。有点模糊,微软确实假设了一个正确的尝试来表达错误信息。试图为无意义的代码想出一个替代方案是很容易的,还有很多事情是有意义的:)呵呵,是的,我同意你的看法。谢谢大家。我需要从进程内存中读取对齐不良的结构。由于我无法记录它的所有字段,所以我尝试使用显式布局。现在我知道这是个坏主意。必须使用带有1字节打包的顺序布局为未知字段创建占位符。但是使用联合将不会如此干净