Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/305.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 当子结构具有LayoutKind.Explicit时,不遵循Sequential_C#_.net_Unsafe_Structlayout_Layoutkind.explicit - Fatal编程技术网

C# 当子结构具有LayoutKind.Explicit时,不遵循Sequential

C# 当子结构具有LayoutKind.Explicit时,不遵循Sequential,c#,.net,unsafe,structlayout,layoutkind.explicit,C#,.net,Unsafe,Structlayout,Layoutkind.explicit,运行此代码时: 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.

运行此代码时:

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)

二,
三,
一,
6
0
四,
五,
垃圾

我在x86上得到了什么:
四,

6
0
二,
三,
一,
四,
五,
垃圾

我在x64上得到的是什么:
八,

6
0
二,
三,
一,
0
四,
五,

更多信息:
-当我删除LayoutKind.Explicit和FieldOffset属性时,问题就消失了。
-当我移除布尔字段时,问题就消失了。
-当我删除长字段时,问题就消失了。
-请注意,在x64上,似乎也忽略了Pack=4属性参数

这适用于.Net3.5和.Net4.0

我的问题:我错过了什么?还是这是一个bug?
我发现了一个类似的问题:

但在我的例子中,即使子结构的属性改变,布局也会改变,而数据类型没有任何改变。因此,它看起来不像是一个优化。此外,我想指出,另一个问题仍然没有答案。
在另一个问题中,他们提到在使用编组时要尊重布局。我自己还没有测试过,但我想知道,既然所有相关属性似乎都已就位,为什么布局不受不安全代码的尊重?文档中是否提到,除非完成编组,否则这些属性将被忽略?为什么?
考虑到这一点,我甚至可以期望LayoutKind.Explicit为不安全代码可靠地工作吗?
此外,文档还提到了保持结构具有预期布局的动机:

<> >为了减少与自动值相关的布局相关问题,C++、VisualBasic和C++编译器指定值类型的顺序布局。



但这一动机显然不适用于不安全代码?

来自MSDN库关于LayoutKind枚举的文章:

根据StructLayoutAttribute.Pack字段的设置,明确控制对象的每个成员在非托管内存中的精确位置。每个成员都必须使用FieldOffsetAttribute来指示该字段在类型中的位置

相关短语突出显示,这并没有发生在这个程序中,指针仍然在很大程度上解除对托管内存的引用

是的,您看到的情况与结构包含DateTime类型的成员时发生的情况相同,DateTime类型应用了[StructLayout(LayoutKind.Auto)]。CLR中用于确定布局的字段封送拆收器代码也致力于遵守托管结构的LayoutKind.Sequential。但是,如果它遇到任何与这一目标相冲突的成员,它将很快毫无怨言地放弃。一个本身不连续的结构就足够了。您可以在src/clr/vm/fieldmarshaler.cpp中看到这一点,搜索
fdisqualifyfromsManagedSequential

这将使它切换到自动布局,与应用于类的布局规则相同。它重新排列字段以最小化成员之间的填充。其净效果是所需的内存量更小。“Bool”成员后有7个字节的填充,未使用的空间用于将“Long”成员与8的倍数地址对齐。当然,这非常浪费,它通过将long作为布局中的第一个成员来修复这个问题

因此,与带/*offset-size*/注释的显式布局不同:

        public int A;        /*  0 - 4 */
        public int B;        /*  4 - 4 */
        public bool Bool;    /*  8 - 1 */
        // padding           /*  9 - 7 */
        public long Long;    /* 16 - 8 */
        public Explicit C;   /* 24 - 8 */
                     /* Total:  32     */ 
它提出了:

        public long Long;    /*  0 - 8 */
        public int A;        /*  8 - 4 */
        public int B;        /* 12 - 4 */
        public bool Bool;    /* 16 - 1 */
        // padding           /* 17 - 3 */
        public Explicit C;   /* 20 - 8 */
                     /* Total:  28     */ 
轻松节省4字节内存。64位布局需要额外的填充,以确保long在存储在数组中时仍然对齐。这些都是高度未记录的,可能会发生更改,请确保永远不要依赖托管内存布局。只有Marshal.StructureToPtr()可以为您提供保证