博主想起刚接触字节对齐时被他支配,受他困扰,知道有字节对齐这么个事儿,但就说不出个所以然。特别是再看看编译器对结构体字节对齐规则的定义:
编译器:结构体对齐规则:
1) 数据类型自身的对齐值:char型数据自身对齐值为1字节,short型数据为2字节,int/float型为4字节,double型为8字节。
2) 结构体的自身对齐值:其成员中自身对齐值最大的那个值。
3) 指定对齐值:#pragma pack (value)时的指定对齐值value。默认是4。
4) 数据成员、结构体的有效对齐值:自身对齐值和指定对齐值中较小者,即有效对齐值=min{自身对齐值,当前指定的pack值}。
这都是些啥?对齐值?min{自身对齐值,当前指定的pack值}? #pragma ?看过了对齐规则感觉没看过对齐规则!
有这个感觉就对了,笔者第一次接触时也一样!
所以“字节对齐”是个啥?真的有这么困难?先不要着急,相信看完这篇文章回过头来看规则你就会恍然大悟,原来这TMD叫字节对齐啊!
为什么会有字节对齐
字节对齐其实是在CPU在读写数据时发生的一次优化。为了方便理解,我们先看看没有字节对齐的情况下CPU在读写数据时都发生了什么?
以32位CPU读取4bytes的数据为例:现在有一个变量A,A的长度为4bytes,A的存放起始地址为0x8001(为了方便举例只展示0x8000~0x08007段的数据)
那么很不幸,32位CPU想要读取A变量的值需要干四件事(注意虽然是四件事,但在底层只用两个读周期):
1)获取0x8000~0x08003段的数据
这里有个问题:为什么CPU不能从0x8001开始取数据?这样咱们不就可以直接获取变量A的数据了吗?
这个问题很重要!因为我们32位计算机是4bytes(32bits)一取的,数据获取的粒度没有这么细,因此CPU在读写数据时为了提高效率都会规定为从特定地址开始操作,而不会从0x8001地址开始读取数据(虽然理论上可以)。简单理解的话就是32位CPU位提高效率都是从4的整数倍的地址开始读取数据的。
2)获取变量A的前3个bytes数据
3)相同地获取0x08000004~0x08000007段的数据
4)获取变量A的最后1个byte数据
经历了四个步骤,咱们终于取到了变量A的数据,那如果我们采用了字节对齐的方式存储变量A呢?
采用了字节对齐后的变量A的起始地址是能被4整除的,因此CPU一次读取就能获取A变量的数据了。
获取0x8000~0x8003段的数据,然后就OK啦
看,是不是方便多了,CPU每少执行一步,也就意味咱们软件的运行效率又高了一分啊!
总的来说:
这一节咱们一起讨论了为什么会有字节对齐以及CPU读取数据的底层逻辑,下一节咱们一起看一看结构体字节是怎么对齐的。
拓展思考:
1、咱们讨论的情况是32位CPU在读写数据时会因为需要优化读写时间而进行字节对齐,那1位CPU(当然这种单bit的上古神器现在肯定是没有使用啦)会出现这种情况吗?64位CPU呢?
2、咱们讨论的情况是在读写4bytes的数据时会因为需要优化读写时间而进行字节对齐,那读写1bytes数据会出现这种情况吗?读写8bytes的数据呢?