幸运星空

Lucker的程序人生

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

熟悉C/C++的朋友都知道,struct类型中的成员在内存中都是按顺序依次存放的,即按成员的声明顺序,并且通常是按成员中占用空间最大的成员进行对齐的。

然而,到了.net托管环境中,则有所不同。CLR为我们提供了两种不同的结构成员内存布局方式:LayoutKind.Sequential和LayoutKind.Explicit,分别实现常用的顺序布局和按偏移量精确布局。前者是CLR的默认值。我们可以在声明struct时加上修饰:[StructLayout(LayoutKind.Sequential)]来告诉CLR要采用的内存布局方式为顺序。采用这种布局方式声明的struct在内存中和非托管环境中声明的struct一致,所以,通常在和非托管Dll进行交互调用的时候,应将struct声明成顺序式的。看下面的示例:

[StructLayout(LayoutKind.Sequential)]
struct S1 //16byte
{
    int i; //4byte
    double b; //8byte
}

按顺序布局的S1本来只占用了12个byte的内存空间,但是,当我们用sizeof(S1)测试的时候,发现它竟然占用了16个byte的空间,这是因为LayoutKind.Sequential(默认)情况下,CLR对struct的Layout的处理方法与C/C++中默认的处理方式相同,即按照结构中占用空间最大的成员进行对齐(Align)。显然,这种方式浪费了一定的内存空间。

然而,按偏移量来布局的方式也有一定的不足,当偏移量计算不准的时候,就会造成数据丢失。请看下面的示例:

[StructLayout(LayoutKind.Explicit)]
struct S2
{
     [FieldOffset(0)] int i;
     [FieldOffset(0)] double b;
}

S2中两个成员的内存位置偏移量都是0,这就意味着它们占用了相同的部分内存空间。当修改其中一个的值时,必然导致另一个的值也发生变化。因为,偏移量的计算应当非常小心才是。以下是一段来MSND上的比较好的例子:

using System.Runtime.InteropServices ;   
[StructLayout(LayoutKind.Explicit, Size=16, CharSet=CharSet.Ansi)] public class MySystemTime { [FieldOffset(0)]public ushort wYear; [FieldOffset(2)]public ushort wMonth; [FieldOffset(4)]public ushort wDayOfWeek; [FieldOffset(6)]public ushort wDay; [FieldOffset(8)]public ushort wHour; [FieldOffset(10)]public ushort wMinute; [FieldOffset(12)]public ushort wSecond; [FieldOffset(14)]public ushort wMilliseconds; }
细心的朋友可能发现:代码中用的是class面不是struct,这说明class和struct其实没有本质的区别。再看一个使用顺序方式的比较好的例子:

      [StructLayout(LayoutKind.Sequential)]
      public   struct   POINT   {
            public   POINT(int   xx,   int   yy)   {   x=xx;   y=yy;   } //构造函数
            public   int   x;
            public   int   y;
            public   override   string   ToString()   {
                  String   s   =   String.Format( "({0},{1}) ",   x,   y);
                  return   s;
            }
      }

补充说明:

其实,.net中还有第三种布局方式:[StructLayout(LayoutKind.Auto)]。这种方式下,CLR会对结构体中的字段顺序进行调整使之占用尽可能少的内存,也就是说,CLR会自动将struct中占用空间多的排在前面,占用空间少的排在后面,并进行4byte的内存对齐,这样下来,可以相比顺序方式节省一定的内存,但还是比精确定义偏移量的方式多浪费一些内存。

有关.net托管环境下struct类型的内存布局的问题,更多信息可以阅读网友happyhippy的博文:
http://www.cnblogs.com/happyhippy/archive/2007/04/12/710927.aspx

《完》

posted on 2010-01-12 10:16  Lucker  阅读(522)  评论(0编辑  收藏  举报