在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. Redis - 环境的安装配置

    Redis 下载安装步骤: 下载 redis 源码: [huey@huey-K42JE ~]$ wget http://download.redis.io/releases/redis-x.y.z.t ...

  2. Java集合类 java.util包

    概述   软件包  类  使用  树  已过时  索引  帮助  JavaTM Platform Standard Ed. 6  上一个软件包   下一个软件包 框架    无框架           ...

  3. 删除mssqlserver表数据,使id从0开始

    ********************************* 注意备份好数据! *************************** 1.删除表数据 delete 表名 2.执行 dbcc c ...

  4. Java Thread UncaughtExceptionHandler

    有没有发生过这样的情况,你写的工作线程莫名其妙的挂了,如果不是被你刚好看到,拿只能抓瞎了,不知道啥原因了,因为异常的时候只会把stack trace打在控制台上,不会记在你想记得错误日志里,头皮都抓破 ...

  5. WCF开发教程资源收集

    WCF开发教程资源收集 1.蒋金楠,网名Artech的博客 [原创]我的WCF之旅(1):创建一个简单的WCF程序[原创]我的WCF之旅(2):Endpoint Overview[原创]我的WCF之旅 ...

  6. java新手笔记34 连接数据库

    1.JdbcUtil package com.yfs.javase.jdbc; import java.sql.Connection; import java.sql.DriverManager; i ...

  7. CCM加密学习

    这几天终于搞定了AES硬件加密工具的使用,几种简单的加密模式也都实验通过了,比较麻烦的一种是CCM模式的加密,它是CTR加密模式和CMAC认证算法的混合使用.本文先介绍CCM模式的原理与基本实现,然后 ...

  8. HR不会告诉你的秘密

    原文转载自http://blog.csdn.net/happy08god/article/details/5534326 下面,只是摘出来一些基本的观点. 1. 入职时的工资高低不重要,只要你努力工作 ...

  9. ALI OSS RequestTimeTooSkewed

    php版阿里oss sdk,请求时抛RequestTimeTooSkewed错误,说时间差距太大,搜了一下发现是服务器的时间设置问题. 我们在安装完Centos Linux操作系统之后,点击系统的时间 ...

  10. Geographic Coordinate Systems

    Coordinate Systems Geographic Coordinate Systems This is an archive of a previous version of the Arc ...