在C/C++中,struct类型中的成员的一旦声明,则实例中成员在内存中的布局(Layout)顺序就定下来了,即与成员声明的顺序相同,并且在默认情况下总是按照结构中占用空间最大的成员进行对齐(Align);当然我们也可以通过设置或编码来设置内存对齐的方式.
        然而在.net托管环境中,CLR提供了更自由的方式来控制struct中Layout:我们可以在定义struct时,在struct上运用StructLayoutAttribute特性来控制成员的内存布局。默认情况下,struct实例中的字段在栈上的布局(Layout)顺序与声明中的顺序相同,即在struct上运用[StructLayoutAttribute(LayoutKind.Sequential)]特性,这样做的原因是结构常用于和非托管代码交互的情形。如果我们正在创建一个与非托管代码没有任何互操作的struct类型,我们很可能希望改变C#编译器的这种默认规则,因此LayoutKind除了Sequential成员之外,还有两个成员Auto和Explicit,给StructLayoutAttribute传入LayoutKind.Auto可以让CLR按照自己选择的最优方式来排列实例中的字段;传入LayoutKind.Explicit可以使字段按照我们的在字段上设定的FieldOffset来更灵活的设置字段排序方式,但这种方式也挺危险的,如果设置错误后果将会比较严重。下面就看几个示例,算下四个struct各占多少Byte?
1.[StructLayout(LayoutKind.Sequential)]

struct StructDeft//C#编译器会自动在上面运用[StructLayout(LayoutKind.Sequential)]
{
bool i;  //1Byte
double c;//8byte
bool b;  //1byte
}

sizeof(StructDeft)得到的结果是24byte!啊哈,本身只有10byte的数据却占有了24byte的内存,这是因为默认(LayoutKind.Sequential)情况下,CLR对struct的Layout的处理方法与C/C++中默认的处理方式相同(8+8+8=24),即按照结构中占用空间最大的成员进行对齐(Align)。10byte的数据却占有了24byte,严重地浪费了内存,所以如果我们正在创建一个与非托管代码没有任何互操作的struct类型,最好还是不要使用默认的StructLayoutAttribute(LayoutKind.Sequential)特性。
2.[StructLayout(LayoutKind.Explicit)]

[StructLayout(LayoutKind.Explicit)]
struct BadStruct
{
    [FieldOffset(0)]
public bool i;  //1Byte
    [FieldOffset(0)]
public double c;//8byte
    [FieldOffset(0)]
public bool b;  //1byte
}

sizeof(BadStruct)得到的结果是9byte,显然得出的基数9显示CLR并没对结构体进行任何内存对齐(Align);本身要占有10byte的数据却只占了9byte,显然有些数据被丢失了,这也正是我给struct取BadStruct作为名字的原因。如果在struct上运用了[StructLayout(LayoutKind.Explicit)],计算FieldOffset一定要小心,例如我们使用上面BadStruct来进行下面的测试:

StructExpt e = new StructExpt();
e.c = 0;
e.i = true;
Console.WriteLine(e.c);

输出的结果不再是0了,而是4.94065645841247E-324,这是因为e.c和e.i共享同一个byte,执行“e.i = true;时”也改变了e.c,CPU在按照浮点数的格式解析e.c时就得到了这个结果.所以在运用LayoutKind.Explicit时千万别吧FieldOffset算错了:)
3.[StructLayout(LayoutKind.Auto)]
        sizeof(StructAuto)得到的结果是12byte。下面来测试下这StructAuto的三个字段是如何摆放的:

unsafe
{
      StructAuto s = new StructAuto();
      Console.WriteLine(string.Format("i:{0}", (int)&(s.i)));
      Console.WriteLine(string.Format("c:{0}", (int)&(s.c)));
      Console.WriteLine(string.Format("b:{0}", (int)&(s.b)));
}
// 测试结果:
i:1242180
c:1242172
b:1242181

即CLR会对结构体中的字段顺序进行调整,将i调到c之后,使得StructAuto的实例s占有尽可能少的内存,并进行4byte的内存对齐(Align),字段顺序调整结果如下图所示:

4.空struct实例的Size

struct EmptyStruct...

无论运用上面LayoutKind的Explicit、Auto还是Sequential,得到的sizeof(EmptyStct)都是1byte。
结论:
        默认(LayoutKind.Sequential)情况下,CLR对struct的Layout的处理方法与C/C++中默认的处理方式相同,即按照结构中占用空间最大的成员进行对齐(Align);
        使用LayoutKind.Explicit的情况下,CLR不对结构体进行任何内存对齐(Align),而且我们要小心就是FieldOffset;
        使用LayoutKind.Auto的情况下,CLR会对结构体中的字段顺序进行调整,使实例占有尽可能少的内存,并进行4byte的内存对齐(Align)。

[转]struct实例字段的内存布局(Layout)和大小(Size)的更多相关文章

  1. 内存对齐与ANSI C中struct型数据的内存布局 【转】

    转自:http://blog.chinaunix.net/uid-25909619-id-3032209.html 当在C中定义了一个结构类型时,它的大小是否等于各字段(field)大小之和?编译器将 ...

  2. 内存对齐与ANSI C中struct型数据的内存布局

    当在C中定义了一个结构类型时,它的大小是否等于各字段(field)大小之和?编译器将如何在内存中放置这些字段?ANSI C对结构体的内存布局有什么要求?而我们的程序又能否依赖这种布局?这些问题或许对不 ...

  3. 浅析内存对齐与ANSI C中struct型数据的内存布局-内存对齐规则

    这些问题或许对不少朋友来说还有点模糊,那么本文就试着探究它们背后的秘密. 首先,至少有一点可以肯定,那就是ANSI C保证结构体中各字段在内存中出现的位置是随它们的声明顺序依次递增的,并且第一个字段的 ...

  4. 一起talk C栗子吧(第一百三十一回:C语言实例--C程序内存布局三)

    各位看官们,大家好.上一回中咱们说的是C程序内存布局的样例,这一回咱们继续说该样例.闲话休提,言归正转.让我们一起talk C栗子吧. 看官们,关于C程序内存布局的样例,我们在前面的两个章回都介绍过了 ...

  5. 查看struct或class的内存布局

    适用于VC编译器(Visual Studio) 附加选项: /d1 reportSingleClassLayout[foo] 例如CItem(注意后面没有空格) /d1 reportSingleCla ...

  6. C# Struct的内存布局

    转载:http://www.csharpwin.com/csharpspace/10454r4891.shtml 问题:请说出以下struct的实例大小以及内存布局 struct Struct1 { ...

  7. Objective-C内存布局

    在我的理解来说: 对象(object)即一块内存,本文要探讨的是一个Objective-C对象在内存的布局(layout)问题,水果的官方文档有说,一个类(class)如果不需要从NSObject继承 ...

  8. 图文详解Java对象内存布局

    作为一名Java程序员,我们在日常工作中使用这款面向对象的编程语言时,做的最频繁的操作大概就是去创建一个个的对象了.对象的创建方式虽然有很多,可以通过new.反射.clone.反序列化等不同方式来创建 ...

  9. JVM总结-java对象的内存布局

    在 Java 程序中,我们拥有多种新建对象的方式.除了最为常见的 new 语句之外,我们还可以通过反射机制.Object.clone 方法.反序列化以及 Unsafe.allocateInstance ...

随机推荐

  1. 让无线网卡同时工作在 AP 和 STA 模式

    这个帖子里的方法有点过时了,不推荐继续使用. 有的时候会碰到这么一种情况,带着电脑和手机出去蹭网,无奈只有一个账号,手机上了电脑就没得用了,电脑用了手机就上不了网.如果能用电脑连接 Wifi 然后再开 ...

  2. Anroid之Intent的使用

    inten常见动作:MAIN_ACTION(主视图). VIEW_ACTION(查看). EDIT_ACTION(修改). PICK_ACTION(选择) .GET_CONTENT_ACTION(获取 ...

  3. 【Android学习】调用系统短信、电话

    今天全心投入Android学习已经有一段时间了,从起初的啥也不懂,到现在半知半解状态,随笔记录些简单且常用的系统功能代码. 调用Android系统短信,其实调用短信非常简单,一个方法就可以搞定.我们可 ...

  4. AjaxFileUpload 在C#中应用

    一.前台页面 <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head ru ...

  5. 关于IE8以上 不引人css 症状

    不知道各位有没有体验过 这样的状况  在同一个文件夹中 <!DOCTYPE html> <html> <head> <meta charset="u ...

  6. 重回cnblogs

    毕业一年,关于工作的想法和思路渐渐充实,是时候回到cnblogs,开始写技术日志了.

  7. 提升程序的特权(AdjustTokenPrivileges)

    首先列出需要的函数 1.OpenProcessToken 2.AdjustTokenPrivileges 3. LookupPrivilegeValue ----------------------- ...

  8. 基于FPGA的按键扫描程序

    最近在学习FPGA,就试着写了个按键扫描的程序.虽说有过基于单片机的按键扫描处理经验,对于按键的处理还是有一些概念.但是单片机程序的编写通常都采用C写,也有用汇编,而FPGA却是采用VHDL或者Ver ...

  9. RHEL6.3 ftp服务器参数的戏说——不看白不看,看了不白看

    大家都知道ftp服务器的应用何止广,简直就是无处不在,配置简单的ftp服务谁都会,无外乎就是刚安装好ftp服务,一启动就ok了:或是达到简单的上传下载修改下/var/ftp/pub的权限,配合配置文件 ...

  10. 从 Java 代码逆向工程生成 UML 类图和序列图

    from:http://blog.itpub.net/14780914/viewspace-588975/ 本文面向于那些软件架构师,设计师和开发人员,他们想使用 IBM® Rational® Sof ...