Chinaunix首页 | 论坛 | 博客
  • 博客访问: 42604
  • 博文数量: 8
  • 博客积分: 165
  • 博客等级: 入伍新兵
  • 技术积分: 96
  • 用 户 组: 普通用户
  • 注册时间: 2011-06-21 16:54
文章分类

全部博文(8)

文章存档

2014年(1)

2013年(1)

2012年(1)

2011年(5)

分类: C/C++

2014-02-27 21:19:32

有时候,我们在程序中会遇到#pragma pack(x)用法,但是,很多时候,搞不清楚这个到底具体作用体现在什么地方,编译器为什么提供这一个预处理命令给我们,我们知道加入这之后会影响结构体中数据的对齐方式,无论在visual studio中还是在gnu中都有相类似的指令,下面就详细介绍一下这个指令到底是如何影响数据对齐的。
首先,准备从理论上给出对齐方式,假设我们这样写,#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了。

下面举一个例子:

点击(此处)折叠或打开

  1. #pragma pack(2)
  2. struct StructDemo
  3. {
  4.     int m_int1;
  5.     char m_char1;
  6.     short m_short1;
  7.     short m_short2;
  8.     int m_int2;
  9.     char m_char2;
  10. };
假设测试平台是intel X86架构的计算机上。
用上面的理论来计算一下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.
大家动手看看这个结构体的大小是不是这样,验证程序如下:

点击(此处)折叠或打开

  1. #include <stdio.h>
  2. #include <stddef.h>
  3. #pragma pack(2)
  4. struct StructDemo
  5. {
  6.     int           m_int1;
  7.     char          m_char1;
  8.     short         m_short1;
  9.     short         m_short2;
  10.     int           m_int2;
  11.     char          m_char2;
  12. };

  13. int main()
  14. {
  15.     int tmp;
  16.     printf("sizeof StructDemo = %d\n", sizeof StructDemo);
  17.     printf("Now, We see which space every member lay\n");
  18.     printf("m_int1 stay at [ %d, %d)\n",                    (tmp = offsetof(StructDemo, m_int1)),            tmp + sizeof(int));
  19.     printf("m_char1 stay at [ %d, %d)\n",                   (tmp = offsetof(StructDemo, m_char1)),           tmp + sizeof(char));
  20.     printf("m_short1 stay at [ %d, %d)\n",                  (tmp = offsetof(StructDemo, m_short1)),          tmp + sizeof(short));
  21.     printf("m_short2 stay at [ %d, %d)\n",                  (tmp = offsetof(StructDemo, m_short2)),          tmp + sizeof(short));
  22.     printf("m_int2 stay at [ %d, %d)\n",                    (tmp = offsetof(StructDemo, m_int2)),            tmp + sizeof(int));
  23.     printf("m_char2 stay at [ %d, %d)\n",                   (tmp = offsetof(StructDemo, m_char2)),           tmp + sizeof(char));
  24.     return 0;
  25. }
运行结果如下图:

从运行结果可以看出跟我们分析的完全一样!
那这种对齐要求有什么用处呢?
1. 在网络传输方面,如果使用#pragma pack(1)来对齐的话会节约网络带宽,虽然会给CPU带来不便,但是我们知道CPU的速度还是远远快于网络的传输速度的
2. 驱动开发方面,特别是在寄存器组的映射方面,是要严格要求的,在此不进行展开,相信从事过驱动开发的人很熟悉
3. CPU处理方面,存取数据速度更快

以上仅仅是个人的一些浅薄的见解,未涉及和有错误之处还请大牛能够指出,本人将非常感谢!


阅读(972) | 评论(0) | 转发(0) |
1

上一篇:valgrind启动分析一

下一篇:没有了

给主人留下些什么吧!~~