C语言结构体的大小 — — 内存对齐和位域
C语言结构体对齐
C语言结构体对齐也是老生常谈的话题了。基本上是面试题的必考题。内容虽然很基础,但一不小心就会弄错。写出一个struct,然后sizeof,你会不会经常对结果感到奇怪?sizeof的结果往往都比你声明的变量总长度要大,这是怎么回事呢?结合网上的资料和自己的编程实践,总结如下。
首先考虑一个问题,为什么要设计内存对齐的处理方式呢?如果体系结构是不对齐的,成员将会一个挨一个存储,显然对齐更浪费了空间。那么为什么要使用对齐呢?体系结构的对齐和不对齐,是在时间和空间上的一个权衡:对齐节省了时间。假设一个体系结构的字长为w,那么它同时就假设了在这种体系结构上对宽度为w的数据的处理最频繁也是最重要的。它的设计也是从优先提高对w位数据操作的效率来考虑的。
既然内存对齐是必要的,那么结构体到底是怎样对齐的呢?
(在没有定义#pragma pack宏的情况下):
原则1、数据成员对齐规则:结构(struct或联合union)的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员按其类型大小和默认对齐参数(32位系统通常默认按4字节对齐)中较小的一个对齐。每个成员的起始地址%每个成员的自身对齐值=0,否则补空直至满足条件。比如对于char型数据,其自身对齐值为1,对于short型为2,对于int型为4,double型在32位机大小为8字节、系统默认对齐方式是4字节,取其较小者,所以其对齐值是4。
原则2、结构体作为成员:如果一个结构里有结构体成员,则按照其成员中自身对齐值最大的那个值对齐。(struct a里存有struct b,b里有char,int,double等元素,那b应该从4的整数倍开始存储。)
原则3、收尾工作:结构体的总大小,也就是sizeof的结果,必须为所用过的最大对齐参数的整数倍,不够就补空字节。比如在32位系统中最大对齐参数是4,如果一个结构中有类型为int、long、float、double等长度等于或大于4的成员,则其总大小必须是4的整数倍。
这三个原则具体怎样理解呢?我们看下面几个例子,通过实例来加深理解。
例1:
typedef struct _A {
short a1;
short a2;
short a3;
}A;
typedef struct _B{
long a1;
short a2;
}B;
sizeof(A) = 6; 这个很好理解,三个short对齐方式都是2。
sizeof(B) = 8; 这个是不是比预想的大2个字节?long为4,short为2,整个为8,因为原则3。
例2:
typedef struct _A{
int a;
char b;
short c;
}A;
typedef struct _B{
char b;
int a;
short c;
}B;
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:typedef struct _A{
int a;
double b;
float c;
}A;
typedef struct _B{
char e[2];
int f;
double g;
short h;
A i;
}B;
sizeof(A) = 16; 这个比较好理解,int为4,double为8,float为4,double对齐方式取了较小的默认方式,按4字节对齐。所以整个A的大小为16。
sizeof(B) = 36; 看看B的内存布局。
e f g h i
B的内存布局:11* *, 1111, 11111111, 11 * * , 1111, 11111111, 1111
i其实就是A的内存布局。i的起始位置要为4的倍数,所以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, 1111111, 11 , 1111, 11111111, 1111
那#pragma pack(2)的结果又是多少呢?#pragma pack(8)呢?留给大家自己思考吧,相信没有问题。
还有一种常见的情况,结构体中含位域字段。位域成员不能单独被取sizeof值。C99规定int、unsigned int和bool可以作为位域类型,但编译器几乎都对此作了扩展,允许其它类型类型的存在。
使用位域的主要目的是压缩存储,其大致规则为:
1) 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;
2) 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;
3) 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,gcc通常采用压缩的方式;
4) 如果位域字段之间穿插着非位域字段,则不进行压缩;
5) 整个结构体的总大小为对齐方式的整数倍。对齐方式去最宽基本类型成员大小和默认对齐方式的较小者。
还是让我们来看看例子。
例4:typedef struct _A{
char f1 : 3;
char f2 : 4;
char f3 : 5;
}A;
a b c
A的内存布局:111, 1111 *, 11111 * * *
位域类型为char,第1个字节仅能容纳下f1和f2,所以f2被压缩到第1个字节中,而f3只能从下一个字节开始。因此sizeof(A)的结果为2。
例5:typedef struct _B{
char f1 : 3;
short f2 : 4;
char f3 : 5;
}B;
由于相邻位域类型不同,在VC6中其sizeof为6,在gcc中为2。
如果是
typedef struct _B{
char f1 : 3;
int f2 : 4;
char f3 : 5;
}B;
在gcc中大小则是4,它们仍然被压缩在了一起,但是长度必须是基本类型int型大小的整数倍;
如果是
typedef struct _B{
char f1 : 3;
long long f2 : 4;
char f3 : 5;
}B;
在32位系统,gcc编译后大小仍然是4,它们仍然被压缩在了一起。虽然long long大小是8,但默认对齐参数是4;
例6:struct C{
char f1 : 3;
char f2;
char f3 : 5;
};
非位域字段穿插在其中,不会产生压缩,在VC6和gcc中得到的大小均为3。
最后顺便提一点,在设计结构体的时候,一般会尊照一个习惯,就是把占用空间小的类型排在前面,占用空间大的类型排在后面,这样可以相对节约一些对齐空间。
---------------------
作者:天街小雨
来源:CSDN
原文:https://blog.csdn.net/nape1006/article/details/19427095
版权声明:本文为博主原创文章,转载请附上博文链接!
C语言结构体的大小 — — 内存对齐和位域相关推荐
- 失传的C结构体打包技艺 -- 内存对齐
作者:Eric S. Raymond 原文链接:http://www.catb.org/esr/structure-packing/ 谁应阅读本文 本文探讨如何通过手工重新打包C结构体声明,来减小内存 ...
- [c/c++] programming之路(28)、结构体存储和内存对齐+枚举类型+typedef+深拷贝和浅拷贝...
一.结构体存储 #include<stdio.h> #include<stdlib.h>struct info{char c; //1 2 4 8 double num; // ...
- 【C语言系列】-结构体中的内存对齐
目录
- C语言结构体的1字节对齐(代码实现)
#include <stdio.h>#define BYTE1 __attribute__((packed, aligned(1))) //aligned(1):1字节对齐typedef ...
- C语言结构体占用内存总结
C语言结构体占用内存总结 前几天有个小朋友问了我一下,关于C语言结构体占用空间的问题.觉得以后会对小可爱有点帮助,就打算先写一下. struct Test {int a;char b;int c; } ...
- 单片机c语言vu8,漫谈C语言结构体
C语言结构体在keilMDK中的运用和注意事项. 漫谈C语言结构体 相信大家对于结构体都不陌生.在此,分享出本人对C语言结构体的学习心得.如果你发现这个总结中有你以前所未掌握的,那本文也算是有点价值了 ...
- 【C语言】一文搞定如何计算结构体的大小----结构体内存对齐规则
要搞定如何正确地计算一个结构体的大小,我们就要深刻理解结构体内存对齐规则: 结构体内存对齐规则: 1.结构体的第一个成员永远在结构体起始位置偏移量为0的位置: 2.结构体成员从第二个成员开始,总是放在 ...
- C语言结构体-大小,对齐,填充,使用及其他
C语言结构体-大小,对齐 C语言中的结构体(struct)的定义 在C语言中,最常用的数据结构就是结构体了,结构体也是其它数据结构(比如链表等)的基础,结构体的使用非常简单. 比如,定义一个结构体: ...
- 内存对齐 | 原来字段顺序还能影响结构体占用的内存空间
大家好啊,不知道看了网管在上周的文章Go指针的使用限制和unsafe.Pointer突破之路,你们有没有感觉Golang 比之前想的还好用呢?确实能直接读写内存,很多以前觉得无能为力的事情就不再是问题 ...
最新文章
- nginc+memcache
- 如何解释混合网络?—Vecloud微云
- boost::checked_delete的测试程序
- Spark 运行模式 standalong yarn
- 阿里巴巴Java开发手册终极版
- 《星辰傀儡线》人物续:“灭世者”、“疯狂者”、“叛逆者”三兄妹
- 爆料图显示iPhone 14 Pro及Max机身更厚 摄像头凸起也更多
- 写给科技公司项目经理的一封警告书
- OpenQA.Selenium.WebDriverException : Failed to start up socket within 45000
- python汉化包放哪_python环境搭建和pycharm的安装配置及汉化(零基础小白版)
- 论文相关------如何在论文写作中使用拉丁文简写
- 图标设计的意思是什么?资深UI设计师告诉你图标的含义!
- 用虚数i与欧拉公式来解释分数阶微积分
- 【名言警句】马太效应
- 前端裁图-如何使用ps软件将矩形图片裁剪成圆角矩形
- 这9个程序员岗位最牛!AI百万年薪夺冠
- APP稳定性测试神器之Monkey-随机事件测试
- 格灵深瞳出手,灵异视频告破
- Microsoft Lync server 2013 企业即时通讯软件
- QQ 空间备份神器,一键备份你所有的青春!
热门文章
- (八)光盘的挂载与解挂、挂载CentOS镜像、rpm安装软件详细学习笔记
- ossim mysql密码_OSSIM 4.1安装详解
- 手机获取百度地图定位
- Oracle 复制表空间 (imp/exp)
- TOGAF架构开发方法 - 使用Visual Paradigm 自动化 过程
- ESP8266 wifi模块学习
- 金属粉末注射成型原理以及特性
- 程序员认知(一)数据、变量、数据类型、内存、表达式、指针、算法、数据结构、拆箱装箱
- java referrer_JavaScript中document.referrer的用法详解
- UVM验证平台搭建一:reg model 生成