概要
众所周知,C语言中的结构体类型的成员,其中若是存在不同类型的变量,则会在编译器优化寻址的过程中,产生缝隙。本文将使用菜鸟教程的C语言在线工具,来进一步研究它的影响。
技术名词解释
#pragma pack() 这条指令主要用于指定字节对齐的方式
括号内的数字必须要是 2 的小幂,否则不会有任何效果。通常我们填的数字都是1,它是2的0次方,其值可以为1,2,4,8,16。如果你指定的对齐字节数,超过了结构体中的最长的那个数据类型,那么它仍然是以最长的那个数据类型对齐的。它所指定的字节对齐是指不同数据类型之间的对齐方式。
下图是指定字节对齐为2时,结构体所占用的内存大小,其余的值依次类推。
技术细节
该在线工具中,是属于64位的,其中 char 是1个字节,short int 是2个字节,int 是4个字节的,float 是4个字节,double 是8个字节。
但在8位单片机中,short int 和 int 的字节长度都是 2 个字节,不要混淆。
前言结束,实验开始。
还是老样子,printf 调试大法。 这里先提一个细节:就是 sizeof 是属于C的关键字,所以你可以不用加括号,打个空格也是可以使用的,它是用来求,在当前系统下的数据类型的大小。
回到正题,按理论来说 char 型和 int 型加起来应该是 5 个字节。但是我们打印出来却是8个字节,那么重点来了,就让我们依次实验一下,编译器在优化寻址的过程中,造成的影响范围到底有多大!
上面这个状态是理想中希望的,实际字节数和打印字节数一致。
惊不惊喜,意不意外?int变量类型的前一段空间和后一段空间都受到了影响,别急,还有更惊喜的呢。
至此,我们已经不难看出,结构体内的成员变量的优化方式,是以最先遇到的最大的那个数据类型来对齐的,但是呢,假如它的前一段或者后一段有剩余没有使用的,那么更小的数据就会被放入这个缝隙,当这个缝隙填满之后,再开辟一个新的和最大数据类型大小一致的空间,无论你的最大类型的数据位置是在前,在后,还是在中间,都是以它来进行对齐。如果你不希望造成更多的空间浪费,建议你将最大的数据类型定义时放在最前面,最小的放在最后面。当然,这个忠告仅对那些不能使用 #pragma pack(1) 指令的编译器生效。
所以像如下这种结构体成员的定义的方式就会非常浪费空间,本来10个字节的数据却使用了24个字节的内存
struct st_aaa
{
char a[1];
double b;
char c[1];
};
当你使用字符串复制函数 strncpy 数据的时候,就会因为这段空白内存而出现bug,更好的复制数据方式应该使用 memcpy 直接搬运内存。
小结
当下,在keil里面。使用#pragma pack(1)指令进行1字节对齐,编译32的可以,但是编译51,或者其他八位机,会报一个被忽略的行的警告,编译器不支持这种指令。
user\main.c(110): warning C245: unknown #pragma, line ignored。
当下国产STC8的单片机性能越逐渐强大,远远超越传统8051,使用的人也很多,所以对于字节对齐的进一步研究还是有必要的,希望本文能帮助到诸君认识到该问题。