- 1、原创力文档(book118)网站文档一经付费(服务费),不意味着购买了该文档的版权,仅供个人/单位学习、研究之用,不得用于商业用途,未经授权,严禁复制、发行、汇编、翻译或者网络传播等,侵权必究。。
- 2、本站所有内容均由合作方或网友上传,本站不对文档的完整性、权威性及其观点立场正确性做任何保证或承诺!文档内容仅供研究参考,付费前请自行鉴别。如您付费,意味着您自己接受本站规则且自行承担风险,本站不退款、不进行额外附加服务;查看《如何避免下载的几个坑》。如果您已付费下载过本站文档,您可以点击 这里二次下载。
- 3、如文档侵犯商业秘密、侵犯著作权、侵犯人身权等,请点击“版权申诉”(推荐),也可以打举报电话:400-050-0827(电话支持时间:9:00-18:30)。
- 4、该文档为VIP文档,如果想要下载,成为VIP会员后,下载免费。
- 5、成为VIP后,下载本文档将扣除1次下载权益。下载后,不支持退款、换文档。如有疑问请联系我们。
- 6、成为VIP后,您将拥有八大权益,权益包括:VIP文档下载权益、阅读免打扰、文档格式转换、高级专利检索、专属身份标志、高级客服、多端互通、版权登记。
- 7、VIP文档为合作方或网友上传,每下载1次, 网站将根据用户上传文档的质量评分、类型等,对文档贡献者给予高额补贴、流量扶持。如果你也想贡献VIP文档。上传文档
查看更多
结构体和联合体 请点评
我们继续用反汇编的方法研究一下C 语言的结构体: 例 19.3. 研究结构体
#include stdio.h
int main(int argc, char** argv)
{
struct {
char a; short b; int c; char d;
} s;
s.a = 1;
s.b = 2;
s.c = 3;
s.d = 4;
printf(%u\n, sizeof(s));
return 0;
}
main 函数中几条语句的反汇编结果如下:
s.a = 1;
80483d5:
c6 45 f0 01
movb
$0x1,-0x10(%ebp)
s.b = 2;
80483d9:
66 c7 45 f2 02 00
movw
$0x2,-0xe(%ebp)
s.c = 3;
80483df:
c7 45 f4 03 00 00 00
movl
$0x3,-0xc(%ebp)
s.d = 4;
80483e6:
c6 45 f8 04
movb
$0x4,-0x8(%ebp)
从访问结构体成员的指令可以看出,结构体的四个成员在栈上是这样排列的: 图 19.5. 结构体的存储布局
虽然栈是从高地址向低地址增长的,但结构体成员也是从低地址向高地址排列的,这一点和
数组类似。但有一点和数组不同,结构体的各成员并不是一个紧挨一个排列的,中间有空隙, 称为填充(Padding),不仅如此,在这个结构体的末尾也有三个字节的填充,所以 sizeof(s)的值是 12。注意,printf 的%u 转换说明表示无符号数,sizeof 的值是 size_t 类型的,是某种无符号整型。
为什么编译器要这样处理呢?有一个知识点我此前一直回避没讲,那就是大多数计算机体系 统结构对于访问内存的指令是有限制的,在32 位平台上,访问 4 字节的指令(比如上面的movl)所访问的内存地址应该是4 的整数倍,访问两字节的指令(比如上面的movw)所访问的内存地址应该是两字节的整数倍,这称为对齐(Alignment)。以前举的所有例子中的内存访问指令都满足这个限制条件,读者可以回头检验一下。如果指令所访问的内存地址没有 正确对齐会怎么样呢?在有些平台上将不能访问内存,而是引发一个异常,在 x86 平台上倒是仍然能访问内存,但是不对齐的指令执行效率比对齐的指令要低,所以编译器在安排各种 变量的地址时都会考虑到对齐的问题。对于本例中的结构体,编译器会把它的基地址对齐到 4 字节边界,也就是说,ebp-0x10 这个地址一定是 4 的整数倍。s.a 占一个字节,没有对齐的问题。s.b 占两个字节,如果 s.b 紧挨在 s.a 后面,它的地址就不能是两字节的整数倍了, 所以编译器会在结构体中插入一个填充字节,使 s.b 的地址也是两字节的整数倍。s.c 占 4 字节,紧挨在s.b 的后面就可以了,因为ebp-0xc 这个地址也是 4 的整数倍。那么为什么s.d 的后面也要有填充位填充到 4 字节边界呢?这是为了便于安排这个结构体后面的变量的地址,假如用这种结构体类型组成一个数组,那么后一个结构体只需和前一个结构体紧挨着排 列就可以保证它的基地址仍然对齐到4 字节边界了,因为在前一个结构体的末尾已经有了填充字节。事实上,C 标准规定数组元素必须紧挨着排列,不能有空隙,这样才能保证每个元素的地址可以按“基地址+n×元素大小”简单计算出来。
合理设计结构体各成员的排列顺序可以节省存储空间,例如上例中的结构体改成这样就可以避免产生填充字节:
struct {
char a; char d; short b; int c;
} s;
此外,gcc 提供了一种扩展语法可以消除结构体中的填充字节:
struct {
char a; short b; int c; char d;
} attribute ((packed)) s;
这样就不能保证结构体成员的对齐了,在访问 b 和c 的时候可能会有效率问题,所以除非有特别的理由,一般不要使用这种语法。
以前我们使用的数据类型都是占几个字节,最小的类型也要占一个字节,而在结构体中还可
以使用 Bit-field 语法定义只占几个 bit 的成员。 下面这个例子出自王聪的网站
( ):例 19.4. Bit-field #include stdio.h
typedef struct {
unsigned int one:1; unsigned int two:3; unsigned int three:10; unsigned int four:5; unsigned int :2; unsigned int five:8; unsigned int six:8;
} demo_typ
文档评论(0)