细说C#中的字段布局

来源:互联网 发布:ddos压力测试软件 编辑:程序博客网 时间:2024/05/23 12:59

     对于值类型,字段在内存中的顺序与我们定义时候的顺序一致。对于引用类型,加载程序会选择最合适的方式进行布局,至于什么事最合适的方式,后面会说。此外C#中有一个叫做StructLayout的特性,可以显示对字段的布局进行更多的控制。下面来细细说说这个特性。

     StructLayout(LayoutKind kind)。

    LayoutKind有三个枚举值:Auto,Explicit,Sequential。Auto告诉CLR,你可以随意的对字段进行布局;Explicit告诉CLR某个字段必须在指定的偏移处,他相当于C,c++中union;Sequential告诉CLR,保持字段定义时候的顺序。对于值类型,默认是Explicit,对于引用类型,默认是Auto。

  

  Explicit:

        当我们使用这个枚举值时,还必须使用一个叫做FieldOffset的特性。实例如下:

     static void Main(string[] args)     {         Char2Int to = new Char2Int();         to.c='a';         Console.WriteLine(i.a);         Console.Read();     }    [StructLayout(LayoutKind.Explicit) ]    public struct Char2Int    {        [FieldOffset(0)]public int a;        [FieldOffset(0)]public char c;    }

编译,运行,输出什么呢?答案是97.。可以发现,这个结构体实现了一个转换功能,既可以将int转换为char,也可以将char转换为int。

        通过使用FieldOffset(0)来将a和c安排在同一个位置,当使用a是,就将值当做int来读,当使用c时,就当做字符来读。当我们为值类型中的字段使用FieldOffset时,为了达到最大的性能,一定要注意地址对齐。简单的说,地址对齐就是字段的地址必须是字段大小的倍数。

        假设a地址y=为x+R。其中x是Char2Int的首地址,R为一个偏移量。如果x是对齐的,那么R对齐则y肯定对齐。事实上,x肯定是对齐的,这一点CLR完全可以保证。那么我们考虑一个字段是否对齐,就需要考虑R是否是字段大小的倍数即可。

  上例中,a的偏移量为0,0是4(int的大小)的倍数,所以字段a是对齐的。c的偏移量是0,0也是1(char的大小)倍数,所以c也对齐。

        可以想象,如果将a的偏移量改为1,那么a就肯定对齐咯。不对齐会引起性能损失,因为cpu需要两个周期来读,等等,这里如果是32机器确实需要两个读周期,如果是64为机器,那么就一个周期咯,因为64为cpu的机器字长是8字节。

     如果字段是引用类型,那么他的偏移量必须是4的倍数(32为cpu)或者8的倍数(64为cpu)。如果不是,那么就会抛出一个TypeLoadException异常。还需要注意的是,这个引用类型不能和非值类型字段重叠,也就是不能string在偏移0处,int也在偏移0处。代码如下

  
     static void Main(string[] args)     {         Int32Stringto = new Int32String();         to.i=12;         Console.Read();     } 


[StructLayout(LayoutKind.Explicit)] public class Int2String { [FieldOffset(0)] public int i; [FieldOffset(0)]public string str; }       

运行这段代码,就会抛出一个TypeLoadException异常,提示引用类型字段不能和值类型字段重叠。如果修改代码如下,也会抛出一个异常,提示引用类型的字段不对齐:

     static void Main(string[] args)     {         Int32Stringto = new Int32String();         to.i=12;         Console.Read();     }   [StructLayout(LayoutKind.Explicit)]    public class Int2String    {        [FieldOffset(0)] public int i;        [FieldOffset(5)]public string str;    }   
注意,这里将str的偏移量修改成了5,这时候肯定不对齐,CLR会抛出一个TypeLoadException。

    需要注意的是,在为32为机器开发的应用程序,到了64为cpu可能会运行不了,这可能是因为指针对齐的原因。譬如,如果我们将str的偏移量修改为4,那么代码在32为cpu上运行良好,在64位cpu就会抛出异常。正确的做法是对所有引用类型的偏移量都设置为8的倍数,因此可以将str的偏移量设置为8,这样代码在32,64都运行良好。


   如何设置字段的偏移量

         如果有很多个字段,如何正确的设置他们的偏移量,同时又保持最小的内存空间呢?可以将占地最大的排在最前面,然后依次是小类型的字段。如果我们的类型中有double和int,char,那么就应当先定义double,然后是int,最后是char。代码如下:

    public struct LayoutDemo    {        public double A;        public int B;        public char C;    }



原创粉丝点击