C语言之字节对齐解析

本文详细解析了C语言中结构体的字节对齐问题,通过实例分析了内存地址的计算规则,解释了为何sizeof的结果与预期不同,并介绍了CPU读取效率与字节对齐的关系。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这两天去面试被连续两次问到的字节对齐的问题,虽然都回答对了,但是回来研究了一下具体的原理,发现自己回答对其实纯属侥幸(当然也是面试官问的相对比较简单,呵呵),不知道以后还会不会被问到类似的问题,现在总结一下,同时也让自己能加深记忆,OK,废话不多说,先上两个面试官问到的问题:

struct myStruct{
    int a;
    char b;
    int c;
    char d;
}ms;
题目大概就是这样,然后问sizeof(ms)的值是多少。

说到这里,如果不太了解字节对齐的朋友可以会说是10。但是,sizeof的结果是16,你可能会感叹:哎呦我次奥,这是为毛。嘎嘎,其实我一开是也是这么感叹的。没关系,听我下面慢慢给你讲解。

既然说到了字节对齐,那首先就要说一下字节对齐的正确方式:

对于标准数据类型,它的地址只要是它的长度的整数倍就行了,而非标准数据类型按下面的原则对齐:
  
  数组 :按照基本数据类型对齐,第一个对齐了后面的自然也就对齐了。 
  联合 :按其包含的长度最大的数据类型对齐。 
  结构体: 结构体中每个数据类型都要对齐

上面说的很清楚了,那么我们现在来分析一下上面的例子:

1,大家应该知道基本类型所占的字节数,即:int占4个字节,char在1个字节

2,这里要明确一下,GNU gcc 也是默认4字节对齐。

3,看到字节对齐的方式关于结构体的那条,即:结构体中每个数据类型都要对齐。

通过上面三点,大家是不是感觉有点似懂非懂的赶脚了,哈哈,不太明白也没关系,听我下面接着说。

我们来对a,b,c,d四个成员一一分析:

首先我们假设结构体的地址是从0x0000开始的(为毛这么假设?后面会讲到。其实不用假设,因为每次都是从%4为0的地址开始的)

1,int a 占4个字节,即:0x0000,0x0001,0x0002,0x0003。

2,char b 占1个字节,即:0x0004。

3,int c 占4个字节,即:0x0008,0x0009,0x000A,0x000B。

4,char d占1个字节,即:0x000C。

看完上面四个成员内存的存放地址,你可能又要发出疑问:“哎呦我次奥,这是为毛”。哈哈,这就是通过内存对齐后出现的内存存储的正确形式。

好,下面我们一条一条的分析这是为毛。

首先,“1,int a 占4个字节,即:0x0000,0x0001,0x0002,0x0003。”这句大家应该都没什么疑问,就是四个联系的字节存放。

其次,“2,char b 占1个字节,即:0x0004。”这个大家应该也没什么疑问,就是一个char的存放,占了一个字节。

再次,“3,int c 占4个字节,即:0x0008,0x0009,0x000A,0x000B。”这个大家应该也没疑问(哈哈,可能大家看到这儿就要有骂人的冲动了:“哎呦我次奥,你这是在逗我们吗?”,哈哈,开个玩笑)这个大家应该很有疑问,为毛这样呢?

大家请往一开始讲到的正确的对齐方式看,你会发现这句话挺有意思的”对于标准数据类型,它的地址只要是它的长度的整数倍就行了“就是这句啦,当然还要加上这句:”2,这里要明确一下,GNU gcc 也是默认4字节对齐。“。

这也就能说明为什么c的内存地址是从0x0008开始,而不是0x0004开始,因为gnu gcc默认是四字节对齐的,这也做是为了保证CPU的读取数据的效率(因为x86架构的cpu一般是一次读取四个字节),假设c是从0x0005开始的,那么,cup读取c的时候就要读取两次了(首先读取0x000--0x0003,然后在读取0x0004--0x0007,最后在读取0x0007之后的字节,而c的存放地址是0x0005--0x0008,所以要读取两次,然后CPU还要去”从两次数据的读取总’拼出‘成员c的值,这样效率就是大打折扣)。

最后,”4,char d占1个字节,即:0x000C。“这句大家应该也有疑问了,因为这样的话,结构体ms的值应该是13而不是16啊,因为char d之后没有数据了为毛还要对齐?我只能说能提出这样疑问的同学是真正看懂上面的解释的好同学。我为你们点32个赞啊,我看好你们哦。

那么,为毛是16而不是13,这就又要大家往上看,大家会看到这句话其实挺有意思”结构体: 结构体中每个数据类型都要对齐。”这句话其实就说明了原因了,因为结构体中最长的是int型的,所以,最后的char d后面也要补三字节进行对齐。这样也就能解释我刚才说到的“首先我们假设结构体的地址是从0x0000开始的(为毛这么假设?后面会讲到。其实不用假设,因为每次都是从%4为0的地址开始的)”这句了,因为结构体每个成员都会对齐,所以,每个结构体的起始地址即:0x0XXX("XXX"代码任意值)%0x0004=0。

OK,大家看到这儿应该也大致了解的字节的自动对齐了,哈哈,今天就讲到这里。不对,我说了有两个道题来着呢,这才一道啊,OK,那我现在就把第二到也提出来,代码如下:

struct myStruct{
    char a;
    int b;
    char c[10];
    
}ms;
大家认为sizeof(ms)的值会是多少?哈哈,是20啦,这个和上面的对齐规则大致相同,当然要好好理解理解这一句”数组 :按照基本数据类型对齐,第一个对齐了后面的自然也就对齐了。“
OK,今天就到这里了,好困,得赶紧碎觉了,大家晚安。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值