首先,准备从理论上给出对齐方式,假设我们这样写,#pragma pack(pack_size),注意这里假设对齐尺寸是pack_size,当然这里是以字节为单位的,毕竟字节是计算机能够存储和修改的最小单位了。紧接着,下面有一个结构体,名字假设为demo,假设里面有n个变量,名字无关紧张,但是大小很是关键,在此,不放设大小分别是demo_member_size1, demo_member_size2, ....., demo_member_sizen, 当然我们现在感兴趣的是sizeof demo是多少的问题,既然是介绍理论方法,就要给出具体的方法,是这么规定的,编译器在从上扫描这个结构体的时候(注意,这个指令是无法影响到结构体中成员的顺序的,而且目前是没有指令能够改变的),会从结构体的开始也就是逻辑0位置开始,向下解析,现在假设解析完了第i-1个成员变量,而此时的偏移量是offset_demo, 紧接着编译器会解析demo结构体的第i个成员变量,从上面我们知道,这个成员变量的尺寸是demo_member_sizei,编译器看到这个尺寸的时候,就会取real_pack_size = min{pack_size, demo_member_sizei},然后计算last_size = offset_demo % real_pack_size,如果last_size刚好等于0,那么不需要进行任何空白填充,而直接进行第i个成员的填充,也就是讲第i个变量放置到这个结构体偏移量为[offset_demo, offset_demo + demo_member_sizei)这个空间范围内,如果last_size不等于0, 此时就需要填充了,以1字节逐渐填充(offset_demo = offset_demo + 1)直到offset_demo % real_pack_size == 0,此时跟上面一样,将第i个成员摆放到[offset_demo, offset_demo + demo_member_sizei)这个空间范围内。一旦摆放好第i个成员变量之后,offset_demo = offset_demo + demo_member_sizei,并以此类推,直到扫描完这个结构体为止。
扫描完这个结构体之后,得到了offset_demo, 可能很多人认为这个值就是sizeof demo了,其实不然,编译器还要做最后一件事情,取real_pack_size = min{max{demo_mem_size1, ..., demo_mem_offsetn}, pack_size},判断此时offset_demo % real_pack_size == 0?,如果等于0,那么offset_demo就是这个结构体的大小,否则的话,编译器还要像上面一样,进行逐字节(offset_demo = offset_demo + 1)填充,直到offset_demo % real_pack_size == 0成立。此时offset_demo的值就是sizeof demo了。
下面举一个例子:
点击(此处)折叠或打开
-
#pragma pack(2)
-
struct StructDemo
-
{
-
int m_int1;
-
char m_char1;
-
short m_short1;
-
short m_short2;
-
int m_int2;
-
char m_char2;
- };
用上面的理论来计算一下sizeof StructDemo的值,很容易发现pack_size = 2, member_size1 = 4, member_size2 = 1, member_size3=3, member_size4 = 2, member_size5 = 4, member_size6 = 1, offset_demo = 0.
接着,我们来充当编译器,首先offset_demo = 0,接着扫描第一个成员m_int1,发现member_size1 = 4, 计算real_pack_size = min{2, 4} = 2,而 offset_demo % 2 = 0 == 0,所以占据空间是[0, 4), 接着offset_demo = offset_demo + member_size1 = 0 + 4 = 4, 接着遇到了m_char1, 4 % 1 == 0, 所以占据空间[4, 5), offset_demo = 5, 紧接着m_short1来了,因为offset_demo % 2 = 5 % 2 != 0, 所以填充,由于6 % 2 == 0, 所以填充了1个字节,offset_demo = 6,m_short1占据空间是[6, 8),offset_demo = 6 + 2 = 8, 当m_short2来了,就简单了,直接存放[8, 10), 接着offset_demo = 10,而此时m_int2存放[10, 14), offset_demo = 14, 此时看到了m_char2, 直接存放[14, 15),最后offset_demo = 15.
注意此时需要计算real_pack_size = min{max{4, 1, 2, 2, 4, 1}, 2} = 2,offset_demo % real_pack_size = 15 % 2 != 0,还需要填充一个字节,最后offset_demo = 16,从而得到sizeof StructDemo = 16.
大家动手看看这个结构体的大小是不是这样,验证程序如下:
点击(此处)折叠或打开
-
#include <stdio.h>
-
#include <stddef.h>
-
#pragma pack(2)
-
struct StructDemo
-
{
-
int m_int1;
-
char m_char1;
-
short m_short1;
-
short m_short2;
-
int m_int2;
-
char m_char2;
-
};
-
-
int main()
-
{
-
int tmp;
-
printf("sizeof StructDemo = %d\n", sizeof StructDemo);
-
printf("Now, We see which space every member lay\n");
-
printf("m_int1 stay at [ %d, %d)\n", (tmp = offsetof(StructDemo, m_int1)), tmp + sizeof(int));
-
printf("m_char1 stay at [ %d, %d)\n", (tmp = offsetof(StructDemo, m_char1)), tmp + sizeof(char));
-
printf("m_short1 stay at [ %d, %d)\n", (tmp = offsetof(StructDemo, m_short1)), tmp + sizeof(short));
-
printf("m_short2 stay at [ %d, %d)\n", (tmp = offsetof(StructDemo, m_short2)), tmp + sizeof(short));
-
printf("m_int2 stay at [ %d, %d)\n", (tmp = offsetof(StructDemo, m_int2)), tmp + sizeof(int));
-
printf("m_char2 stay at [ %d, %d)\n", (tmp = offsetof(StructDemo, m_char2)), tmp + sizeof(char));
-
return 0;
- }

从运行结果可以看出跟我们分析的完全一样!
那这种对齐要求有什么用处呢?
1. 在网络传输方面,如果使用#pragma pack(1)来对齐的话会节约网络带宽,虽然会给CPU带来不便,但是我们知道CPU的速度还是远远快于网络的传输速度的
2. 驱动开发方面,特别是在寄存器组的映射方面,是要严格要求的,在此不进行展开,相信从事过驱动开发的人很熟悉
3. CPU处理方面,存取数据速度更快
以上仅仅是个人的一些浅薄的见解,未涉及和有错误之处还请大牛能够指出,本人将非常感谢!