C编程-字节对齐(上)

Posted by 周思进 on December 30, 2019

日常工作对于结构体编写,基本都会进行人为的字节补齐操作,比如如下结构体

struct student{
    char name[32];
    char pw[32];
    char age;
    char res[3];
    int  money;
}

中间的res[3]就是显示进行字节对齐操作,如果不进行显示对齐,很有可能造成后面新开发的同事在此结构体上新增字段未注意原有字节大小而造成字节不对齐的现象。

而如果字节不对齐,了解的最基本问题就是会影响读取效率,比如在32位系统下,原本一个32位的整型变量如果在字节对齐的地址开始存放,那CPU只需针对那个地址进行一次读取操作即可,而如果是字节不对齐存储,比如前一个地址的后2个字节和下一个地址的前两个字节进行存储,则CPU就需要读取两次进行组合得到所要的数据。

另外一点就是字节不对齐,对于部分系统而言,访问字节未对齐的地址变量,可能会发生出错,这对于代码可移植性也不好。所以如下代码,在开发中如有存在,也是不推荐的

struct student{
    char name[32+1];
    char pw[32+1];
    char age;
    char res;
    int  money;
}

首先这样写目的是明确的,就是为了保证name是\0结尾的,避免程序代码中因为name设置最大32字节的情况下,该变量没有\0结尾而存在的各种字符串操作引起的越界问题。

不过我个人觉得,这样还不如需求明确只能支持31字节反而好一点。

上面结构体res字段则是设置的是1个预留字节,而非原先的3个预留字节了,如果不注意的,也可能不会去管字节对齐问题,money变量也就很可能是非字节对齐了。

不过也需要说明的是,即使你没有显示预留res,一般编译器也会帮你中间进行字节对齐操作,这也是经常面试会考到的问题。

那是怎么计算的呢?主要就两条规则:
1、下一个变量的起始地址需满足前面变量所占字节数是该变量自身字节长度的倍数
2、整个结构体最终字节对齐后的长度是其成员变量中最大那个的整数倍(考虑结构体数组的情况)

比如如下结构体在32位系统下sizeof是多大呢?

struct test {
    char one;
    short two;
    long long eight;
    int four;
}

我们按上述规则来计算下,第一个变量one,char型,1个字节,然后看第二个变量two,short型,2字节,根据第一条规则,则前面变量需要额外预留一个字节,这样加上two后就是4个字节。
然后eight变量是long long类型,8字节,同样根据第一条规则,那么two后面还需要预留4个字节,这样加上eight就是16个字节了。
最后一个four变量,因为前面16个字节已经满足第一条规则,加上four变量最后就是20个字节。
然后看整个结构体大小再看第二条规则,需要是成员变量最大的整数倍,也就是8的整数倍,所以four后面还会补齐4个字节,最终该结构体大小就是24字节。

了解了计算规则,所以在平时设计构体时,能将小字节的放在一起以满足字节对齐需要,也就可以省一定的内存空间。