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)]
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()]
public bool i; //1Byte
)]
public double c;//8byte
)]
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 ;
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)));
}
// 测试结果:
c:
b:即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)。
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 ...
随机推荐
- STOMP协议规范【转】
STOMP协议规范英文原文:http://stomp.github.io/stomp-specification-1.2.html STOMP协议规范译文原文:http://simlegate.com ...
- 解决VS2015中出现类似于error C4996: 'scanf': This function or variable may be unsafe的安全检查错误
用习惯了VS老版本的人当刚使用VS2013的时候可能总遇到类似于这样的错误: error C4996: 'scanf': This function or variable may be unsafe ...
- [Vue]使用axios实现ajax请求
1.定义myAjax export const myAjax=function createHttpClient(ajaxConfig) { let httpClient = null; if ...
- 开发H5游戏引擎的选择:Egret或Laya?
开发H5游戏引擎的选择:Egret或Laya? 一.总结 一句话总结:选laya吧 二.开发H5游戏引擎的选择:Egret或Laya? 一.H5游戏开发的引擎介绍 开发H5游戏的引擎有很多,比如egr ...
- Jenkins搭建GitHub仓库
一.安装Jenkins 二.安装Git 三.注册GitHub账号 以上都很简单,网上资源丰富 四.新增GitHub远程仓库,连接到本地Git,这样就可以实现代码提交到GitHub中了 ----自行找资 ...
- SOUI中启用拖文件
本文所用SOUI版本为1.0版本,在拖文件上与一般的消息略有不同. 1.添加拖文件消息响应 先与常规添加消息相同. class CMainFrm : public SHostWnd { public: ...
- Pandas:SettingWithCopyWarning
当我为数据增加新的列的时候,Pandas一直报错:SettingWithCopyWarning 这个问题没有很好地解决方法,因为这个问题也无关大碍,不让Pandas报错就行了. 解决方法: pd.op ...
- MySQL索引优化详解
MySQL存储引擎简介 查看命令 a. 查看所使用的MySQL现在已提供什么存储引擎: mysql> show engines; b. 查看所使用的MySQL当前默认的存储引擎: mysql&g ...
- LeetCode OJ:Implement Queue using Stacks(栈实现队列)
比较典型的一个题目,easy,不过可以有许多实现方式. 这里用的方式是每次pop完成之后再将stack2中的内容立即倒回stack1中.但是其他的实现也可以不是这样,可以是需要push的时候检查再,如 ...
- 剑指offer--47.数据流中的中位数
时间限制:1秒 空间限制:32768K 热度指数:122511 算法知识视频讲解 题目描述 如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值.如 ...