C#-StructLayoutAttribute(结构体布局)
struct实例字段的内存布局(Layout)和大小(Size)
在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)]






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)]










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




输出的结果不再是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的三个字段是如何摆放的:











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

无论运用上面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)。
C#-StructLayoutAttribute(结构体布局)的更多相关文章
- 如何用vs查看结构体布局
今天遇到一个问题: 假设在每个系统的structA 结构不同,我们在windbg看了以后直接拿来用,自己定义成结构体,如何来验证这个结构体内存布局是否和windbg一致. 当然笨办法是自己一个个成员数 ...
- go语言之行--结构体(struct)详解、链表
一.struct简介 go语言中没有像类的概念,但是可以通过结构体struct实现oop(面向对象编程).struct的成员(也叫属性或字段)可以是任何类型,如普通类型.复合类型.函数.map.int ...
- 飞控入门之C语言结构体、枚举
结构体 先来说明一下,结构体存在的意义.比如说有一只猫,要在C语言程序中综合描述它,那么可以这样说,它的体重是float类型的,颜色是char类型的,它的一些食物名字是一个数组,那么如果分开定义这些变 ...
- VC中结构体的内存布局
看了 VC++中内存对齐 这篇文章,感觉说复杂了,根据我的总结,要算出结构体的内存大小和偏移量,只要清楚结构体各成员的内存布局就行了,下面介绍一下我总结的规则,有不对之处,欢迎回复. 1.实际PACK ...
- C语言结构体及其内存布局
code[class*="language-"], pre[class*="language-"] { color: rgba(51, 51, 51, 1); ...
- C#调用C++ dll时,结构体引用传参的方法
写了一个C++的LogLog Logit 四参数等算法的接口dll,给C#调用,但是发现传参有问题 如 extern "C" _declspec(dllexport) bool ...
- C#结构体和字节数组的转换函数
在通信过程中,一般我们都会操作到字节数组.特别是希望在不同语言编程进行操作的时候. 虽然C#提供了序列化的支持,不用字节数组也行.但操作字节数组肯定会碰到. 一般都会采用结构来表示字节数组.但结构 ...
- .NET C#基础(5):结构体 - 高性能代码的基石
0. 文章目的 本文面向有一定.NET C#基础知识的学习者,介绍C#中结构体定义.使用以及特点. 1. 阅读基础 了解C#基本语法 了解.NET中的栈与托管堆 2. 值类型 2.1 .N ...
- C# Struct结构体里数组长度的指定
typedef struct Point{ unsigned short x; unsigned short y; }mPoint;//点坐标 typedef struct Line{ mPoint ...
随机推荐
- 【Python】“UnicodeDecodeError: 'ascii' codec can't decode byte 0xe9”根因及解决方法
背景 自动化测试调用HTMLTestRunner生成测试报告的时候,出现了编码错误,错误如题 原因 搜索了很多资料,得出的结论是python的str默认是ascii编码,和unicode编码冲突,就会 ...
- Java连接MySQL数据库——代码
工具:eclipse MySQL5.7.17 MySQL连接驱动:mysql-connector-java-5.1.43.jar 加载驱动:我是用MAVEN进行管理 数据库连接信息: 数据库名称:wu ...
- msdia80.dll文件出现在磁盘根目录下的解决方案(转)
情况描述: 当安装某些软件后,磁盘根目录中多出了msdia80.dll文件,该文件显示为2006年12月1日,884KB. 原因: 当使用64位操作系统时,在电脑上安装 Microsoft Vis ...
- STL学习笔记(转,还是比较全的)
STL简介 1 概况 2 1.1 STL是什么 2 1.2 为什么我们需要学习STL 2 1.3 初识STL 2 1.4 STL 的组成 5 2 容器 6 2.1 基本容器——向量(vector) 6 ...
- IOS-UIButton的文本与图片的布局
UIButton内部文本和图片的布局是我们日常代码中,不可缺少的部分,按钮默认左边图片右边文本,那要实现左边文本,右边图片,我们该怎么解决呢,上面图片,下面文本又该怎么办呢 其实很简单,今天总结下,目 ...
- vue 跨域
注意!只能在本地调试使用,上线后url会出错使用以下方法要先引入网络模块 先配置文件:config =>index.js以下部分改为:proxyTable: { '/apis': { // 测试 ...
- [转载]Java读取Excel中的单元格数据
目前网上能找到的读取Excel表格中数据的两种比较好的方案:PageOffice好用开发效率高:POI免费.供大家参考,针对具体情况选择具体方案. 1. PageOffice读取excel impor ...
- C++:创建线程初试
1.使用CreatThread创建 #include <iostream> #include <Windows.h> using namespace std; /* 创建一个线 ...
- hdu 1079 Calendar Game sg函数 难度:0
Calendar Game Time Limit: 5000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Tot ...
- Docker的安装及操作
1. 在Ubuntu中安装Docker 更新ubuntu的apt源索引 sudo apt-get update 安装包允许apt通过HTTPS使用仓库 sudo apt-get install \ a ...