C语言-结构体内存对齐
C语言结构体对齐也是老生常谈的话题了。基本上是面试题的必考题。内容虽然很基础,但一不小心就会弄错。写出一个struct,然后sizeof,你会不会经常对结果感到奇怪?sizeof的结果往往都比你声明的变量总长度要大,这是怎么回事呢?
开始学的时候,也被此类问题困扰很久。其实相关的文章很多,感觉说清楚的不多。结构体到底怎样对齐?
有人给对齐原则做过总结,具体在哪里看到现在已记不起来,这里引用一下前人的经验(在没有#pragma pack宏的情况下):
原则1、数据成员对齐规则:结构(struct或联合union)的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储)。
原则2、结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。(struct a里存有struct b,b里有char,int,double等元素,那b应该从8的整数倍开始存储。)
原则3、收尾工作:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补齐。
这三个原则具体怎样理解呢?我们看下面几个例子,通过实例来加深理解。
例1:
struct {
short a1;
short a2;
short a3;
} A;
struct{
long a1;
short a2;
} B;
sizeof(A) = 6; 这个很好理解,三个short都为2。
sizeof(B) = 8; 这个比是不是比预想的大2个字节?long为4,short为2,整个为8,因为原则3。
例2:
struct A{
int a;
char b;
short c;
};
struct B{
char b;
int a;
short c;
};
sizeof(A) = 8; int为4,char为1,short为2,这里用到了原则1和原则3。
sizeof(B) = 12; 是否超出预想范围?char为1,int为4,short为2,怎么会是12?还是原则1和原则3。
深究一下,为什么是这样,我们可以看看内存里的布局情况。
a b c
A的内存布局:1111,
1*,
11
b
a
c
B的内存布局:1***,
1111, 11**
其中星号*表示填充的字节。A中,b后面为何要补充一个字节?因为c为short,其起始位置要为2的倍数,就是原则1。c的后面没有补充,因为b和c正好占用4个字节,整个A占用空间为4的倍数,也就是最大成员int类型的倍数,所以不用补充。
B中,b是char为1,b后面补充了3个字节,因为a是int为4,根据原则1,起始位置要为4的倍数,所以b后面要补充3个字节。c后面补充两个字节,根据原则3,整个B占用空间要为4的倍数,c后面不补充,整个B的空间为10,不符,所以要补充2个字节。
再看一个结构中含有结构成员的例子:
例3:
struct A{
int a;
double b;
float c;
};
struct B{
char e[];
int f;
double g;
short h;
struct A i;
};
sizeof(A) = 24; 这个比较好理解,int为4,double为8,float为4,总长为8的倍数,补齐,所以整个A为24。
sizeof(B) = 48; 看看B的内存布局。
e f g h i
B的内存布局:11*
*,
1111, 11111111, 11 * * * * *
*,
1111* * * *, 11111111, 1111 * * * *
i其实就是A的内存布局。i的起始位置要为24的倍数,所以h后面要补齐。把B的内存布局弄清楚,有关结构体的对齐方式基本就算掌握了。
以上讲的都是没有#pragma
pack宏的情况,如果有#pragma pack宏,对齐方式按照宏的定义来。比如上面的结构体前加#pragma
pack(1),内存的布局就会完全改变。sizeof(A) = 16; sizeof(B) = 32;
有了#pragma
pack(1),内存不会再遵循原则1和原则3了,按1字节对齐。没错,这不是理想中的没有内存对齐的世界吗。
a
b
c
A的内存布局:1111,
11111111, 1111
e
f
g
h
i
B的内存布局:11,
1111, 11111111, 11
,
1111, 11111111, 1111
那#pragma pack(2)的结果又是多少呢?#pragma pack(4)呢?留给大家自己思考吧,相信没有问题。
还有一种常见的情况,结构体中含位域字段。位域成员不能单独被取sizeof值。C99规定int、unsigned
int和bool可以作为位域类型,但编译器几乎都对此作了扩展,允许其它类型类型的存在。
使用位域的主要目的是压缩存储,其大致规则为:
1)
如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;
2)
如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;
3) 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式,Dev-C++采取压缩方式;
4) 如果位域字段之间穿插着非位域字段,则不进行压缩;
5) 整个结构体的总大小为最宽基本类型成员大小的整数倍。
还是让我们来看看例子。
例4:
struct A{
char f1 : ;
char f2 : ;
char f3 : ;
};
a b c
A的内存布局:111,
1111 *, 11111 * * *
位域类型为char,第1个字节仅能容纳下f1和f2,所以f2被压缩到第1个字节中,而f3只能从下一个字节开始。因此sizeof(A)的结果为2。
例5:
struct B{
char f1 : ;
short f2 : ;
char f3 : ;
};
由于相邻位域类型不同,在VC6中其sizeof为6,在Dev-C++中为2。
例6:
struct C{
char f1 : ;
char f2;
char f3 : ;
};
非位域字段穿插在其中,不会产生压缩,在VC6和Dev-C++中得到的大小均为3。
考虑一个问题,为什么要设计内存对齐的处理方式呢?如果体系结构是不对齐的,成员将会一个挨一个存储,显然对齐更浪费了空间。那么为什么要使用对齐呢?体 系结构的对齐和不对齐,是在时间和空间上的一个权衡。对齐节省了时间。假设一个体系结构的字长为w,那么它同时就假设了在这种体系结构上对宽度为w的数据 的处理最频繁也是最重要的。它的设计也是从优先提高对w位数据操作的效率来考虑的。有兴趣的可以google一下,人家就可以跟你解释的,一大堆的道理。
最后顺便提一点,在设计结构体的时候,一般会尊照一个习惯,就是把占用空间小的类型排在前面,占用空间大的类型排在后面,这样可以相对节约一些对齐空间。
C语言-结构体内存对齐的更多相关文章
- go语言结构体内存对齐
cpu要想从内存读取数据,需要通过地址总线,把地址传输给内存,内存准备好数据,输出到数据总线,交给cpu,如果地址总线只有8根,那这个地址就只有8位可以表示[0,255]256个地址,因为表示不了更多 ...
- 【APUE】Chapter17 Advanced IPC & sign extension & 结构体内存对齐
17.1 Introduction 这一章主要讲了UNIX Domain Sockets这样的进程间通讯方式,并列举了具体的几个例子. 17.2 UNIX Domain Sockets 这是一种特殊s ...
- 关于结构体内存对齐方式的总结(#pragma pack()和alignas())
最近闲来无事,翻阅msdn,在预编译指令中,翻阅到#pragma pack这个预处理指令,这个预处理指令为结构体内存对齐指令,偶然发现还有另外的内存对齐指令aligns(C++11),__declsp ...
- C++ struct结构体内存对齐
•小试牛刀 我们自定义两个结构体 A 和 B: struct A { char c1; char c2; int i; double d; }; struct B { char c1; int i; ...
- C语言结构体内存分配详情
#include <stdio.h> int main() { /*************************************************** * * 结构体内存 ...
- [C/C++] 结构体内存对齐用法
一.为什么要内存对齐 经过内存对齐之后,CPU的内存访问速度大大提升; 内存空间按照byte划分,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定变量的时候经常在特定的内 ...
- [C/C++] 结构体内存对齐:alignas alignof pack
简述: alignas(x):指定结构体内某个成员的对齐字节数,指定的对齐字节数不能小于它原本的字节数,且为2^n; #pragma pack(x):指定结构体的对齐方式,只能缩小结构体的对齐数,且为 ...
- c 结构体内存对齐详解
0x00简介 首先要知道结构体的对齐规制 1.第一个成员在结构体变量偏移量为0的地址处 2.其他成员变量对齐到某个数字的整数倍的地址处 对齐数=编辑器默认的一个对齐数与该成员大小的较小值 vs中默认的 ...
- C语言中结构体内存对齐
先写一个小程序: #include<stdio.h> struct student { int a; char k; short m; }; int main() { st ...
随机推荐
- JZYZOJ1536 [haoi2014]走出金字塔
http://172.20.6.3/Problem_Show.asp?id=1536 确实不难,找规律的题,开始想复杂了,分了好多情况.开始为省几个变量加了一大堆max,min,abs代码一下子复杂太 ...
- 【拓扑排序】【DFS】Painting A Board
[poj1691]Painting A Board Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 3902 Accept ...
- BZOJ 2288 【POJ Challenge】生日礼物(贪心+优先队列)
[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=2288 [题目大意] 给出一列数,求最多取m段连续的数字,使得总和最大 [题解] 首先我 ...
- 【差分约束系统】【强连通分量缩点】【拓扑排序】【DAG最短路】CDOJ1638 红藕香残玉簟秋,轻解罗裳,独上兰舟。
题意: 给定n个点(点权未知)和m条信息:u的权值>=v的权值+w 求点权的极小解和极大解(无解则输出-1) 极小解即每个点的点权可能的最小值 极大解即每个点的点权可能的最大值 题解: 差分约束 ...
- 【FFT(母函数)+容斥】BZOJ3771-Triple
[题目大意] 给出 n个物品,价值为别为Xi且各不相同,现在可以取1个.2个或3个,问每种价值和有几种情况? *顺序不同算一种 [思路] 显然是个母函数,A表示每种物品取一个的情况,B表示每种物品取二 ...
- 泛型类Bag
课堂练习--泛型类Bag 要求: 0.代码运行在命令行中,路径要体现学号信息,IDEA中,伪代码要体现个人学号信息 1.参见Bag的UML图,用Java继承BagInterface实现泛型类Bag,并 ...
- struts2和spring整合错误 org.springframework.beans.factory.BeanCreationException,已解决
先贴上错误 2018-8-16 23:41:10 org.springframework.context.support.ClassPathXmlApplicationContext prepareR ...
- 【转】记录PHP、MySQL在高并发场景下产生的一次事故
看了一篇网友日志,感觉工作中值得借鉴,原文如下: 事故描述 在一次项目中,上线了一新功能之后,陆陆续续的有客服向我们反应,有用户的个别道具数量高达42亿,但是当时一直没有到证据表示这是,确实存在,并且 ...
- Microsoft SQL Server 2012 Internals
http://blog.csdn.net/column/details/learnsqlserver2012.html
- zk常见面试题
一个客户端修改了某个节点的数据,其它客户端能够马上获取到这个最新数据吗 ZooKeeper不能确保任何客户端能够获取(即Read Request)到一样的数据,除非客户端自己要求:方法是客户端在获取数 ...