使用 FieldOffset 意外行为的结构
Posted
技术标签:
【中文标题】使用 FieldOffset 意外行为的结构【英文标题】:structs using FieldOffset unexpected behaviour 【发布时间】:2011-07-04 00:57:24 【问题描述】:我正在尝试理解显式结构布局和结构覆盖,但我没有看到我期望的行为。给定以下代码:
class Program
static void Main(string[] args)
byte[] bytes = new byte[17];
bytes[0] = 0x01; // Age is 1 //IntField1
bytes[1] = 0x00; //IntField1
bytes[2] = 0x00; //IntField1
bytes[3] = 0x00; //IntField1
bytes[4] = 0x02; //IntField2
bytes[5] = 0x00; //IntField2
bytes[6] = 0x00; //IntField2
bytes[7] = 0x00; //IntField2
bytes[8] = 0x41; //CharArray A
bytes[9] = 0x42; //CharArray B
bytes[10] = 0x43; //CharArray C
bytes[11] = 0x44; //CharArray D
bytes[12] = 0x45; //CharArray E
bytes[13] = 0x46; //CharArray F
bytes[14] = 0x00; // \0 decimal 0
bytes[15] = 0x00; // \0 decimal 0
bytes[16] = 0x01; // 1 decimal 1
Console.WriteLine(Marshal.SizeOf(typeof(TestStruct)));
TestStruct testStruct2 = (TestStruct) RawDeserialize(bytes, 0, typeof (TestStruct));
Console.WriteLine(testStruct2);
Console.ReadLine();
public static object RawDeserialize( byte[] rawData, int position, Type anyType )
int rawsize = Marshal.SizeOf( anyType );
if( rawsize > rawData.Length )
return null;
IntPtr buffer = Marshal.AllocHGlobal( rawsize );
Marshal.Copy( rawData, position, buffer, rawsize );
object retobj = Marshal.PtrToStructure( buffer, anyType );
Marshal.FreeHGlobal( buffer );
return retobj;
[StructLayout(LayoutKind.Explicit, Pack = 1)]
public struct TestStruct
[FieldOffset(0)]
public int IntField1;
[FieldOffset(4)]
public int IntField2;
[FieldOffset(8)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public char[] CharArray;
[FieldOffset(16)]
public byte SomeByte;
[FieldOffset(8)]
public TestStruct2 SubStruct;
public override string ToString()
return string.Format("IntField1: 0\nIntField2: 1\nCharArray: 2\nSomeByte: 3\nSubStruct:\n4",
IntField1, IntField2, new string(CharArray), SomeByte, SubStruct);
[StructLayout(LayoutKind.Explicit, Pack = 1)]
public struct TestStruct2
[FieldOffset(0)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public char[] CharArray1;
[FieldOffset(0)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public char[] CharArray2;
[FieldOffset(4)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public char[] CharArray3;
public override string ToString()
return string.Format("CharArray1: 0\nCharArray2: 1\nCharArray3: 2",
new string(CharArray1), new string(CharArray2), new string(CharArray3));
我希望这样的结果是这样的:
IntField1: 1 IntField2: 2 字符数组:ABCDEF SomeByte:1 子结构: CharArray1:ABCDEF CharArray2: ABCD CharArray3: E
但结果是:
IntField1: 1 IntField2: 2 字符数组:ABCD SomeByte:1 子结构: CharArray1: ABCD CharArray2: ABCD CharArray3: EF
为什么 TestStruct 中的 CharArray 的长度是 4?我预计它有 6 个字符 ABCDEF,但它只包含 ABCD。 TestStruct2.CharArray1 也一样。
【问题讨论】:
我发现这篇文章很有帮助:m.developerfusion.com/article/84519/mastering-structs-in-c 谢谢。我实际上读过它,但仍然不明白我所看到的。也许我需要再仔细阅读一下...... 【参考方案1】:通过将 TestStruct2 放在 CharArray 之后但在相同的偏移量处,现在指向 TestStruct2 的 CharArray2 的指针正在覆盖过去指向 TestStruct 自己的 chararray 的指针。
如果您注释掉或更改 TestStruct2 的 CharArray2 的长度,您将看到预期的结果。
同样观察当你把 struct2 放在首位时会发生什么,即:
[StructLayout(LayoutKind.Explicit, Pack = 1)]
public struct TestStruct
[FieldOffset(8)]
public TestStruct2 SubStruct;
[FieldOffset(0)]
public int IntField1;
[FieldOffset(4)]
public int IntField2;
[FieldOffset(8)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public char[] CharArray;
[FieldOffset(16)]
public byte SomeByte;
public override string ToString()
return string.Format("IntField1: 0\nIntField2: 1\nCharArray: 2\nSomeByte: 3\nSubStruct:\n4",
IntField1, IntField2, new string(CharArray), SomeByte, SubStruct);
现在效果反转了,TestStruct2 的 CharArray2 有六个字符长。
【讨论】:
哇非常有趣!最好了解发生这种情况的原因。不过谢谢你。 因为内存是重叠的:你试图让相同的内存位置(偏移 8:11)指向两个(实际上是三个)不同的东西,一个大小为 4 的数组和 [two]大小为 6 的数组[s]。如果您更改 CharArray2 的长度,您会看到其他的也发生变化。【参考方案2】:要记住的一点是,C# 中的 char 是 2 个字节的 unicode 字符。
虽然在保留子结构的同时我仍然无法获得预期的结果。重叠的 SizeConst 数组似乎把事情搞砸了。
【讨论】:
【参考方案3】:char[] 是一个引用类型,它的大小是一个 IntPtr,可以是 4 或 8 字节 - 取决于平台(x86 或 x64),并且它的值不会存储在结构中。
MarshalAs 属性不会改变信息在结构中的存储方式,只会改变信息的转换方式(例如,与非托管代码之间的转换方式)。
【讨论】:
以上是关于使用 FieldOffset 意外行为的结构的主要内容,如果未能解决你的问题,请参考以下文章