您的位置:首页 > 职场人生

关于内存对界

2014-02-05 17:36 190 查看
本文节选自宋宝华的C/C++的struct深层探索一文,本人对其所描述的struct对齐比较喜欢,为此转来与大家分享,原文见http://blog.donews.com/21cnbao/archive/2005/09/08/544877.aspx

 

Intel 、微软等公司曾经出过一道类似的面试题:

1. #include <iostream.h>

2. #pragma pack(8)

3. struct example1

4. {

5.     short a;

6.     long b;

7. };

8. struct example2

9. {

10.          char
c;

11.          example1
struct1;

12.          short
e;

13. };

14. #pragma pack()

 

15. int main(int argc, char* argv[])

16. {

17.   example2 struct2;

18.   cout << sizeof(example1) << endl;

19.   cout << sizeof(example2) << endl;

20.   cout << (unsigned int)(&struct2.struct1) - (unsigned int)(&struct2)

  << endl;

21.   return 0;

22. }

问程序的输入结果是什么?

答案是:

8

16

4

不明白?还是不明白?下面一一道来:

1 、 自然对界

struct 是一种复合数据类型,其构成元素既可以是基本数据类型(如 int 、 long 、 float 等)的变量,也可以是一些复合数据类型(如 array 、 struct 、 union 等)的数据单元。对于结构体,编译器会自动进行成员变量的对齐,以提高运算效率。缺省情况下,编译器为结构体的每个成员按其自然对界( natural
alignment )条件分配空间。各个成员按照它们被声明的顺序在内存中顺序存储,第一个成员的地址和整个结构的地址相同。

自然对界 (natural alignment) 即默认对齐方式,是指按结构体的成员中 size 最大的成员对齐。

例如:

struct naturalalign

{

char a;

short b;

char c;

};

在上述结构体中, size 最大的是 short ,其长度为 2 字节,因而结构体中的 char 成员 a 、 c 都以 2 为单位对齐, sizeof(naturalalign) 的结果等于 6 ;

如果改为:

struct naturalalign

{

char a;

int b;

char c;

};

其结果显然为 12 。

 

2 、 指定对界

一般地,可以通过下面的方法来改变缺省的对界条件:

· 使用伪指令 #pragma
pack (n) ,编译器将按照 n 个字节对齐;

· 使用伪指令 #pragma
pack () ,取消自定义字节对齐方式。

注意:如果 #pragma pack (n) 中指定的 n 大于结构体中最大成员的 size ,则其不起作用,结构体仍然按照 size 最大的成员进行对界。

例如:

#pragma pack (n)

struct naturalalign

{

char a;

int b;

char c;

};

#pragma pack ()

当 n 为 4 、 8 、 16 时,其对齐方式均一样, sizeof(naturalalign) 的结果都等于 12 。而当 n 为 2时,其发挥了作用,使得 sizeof(naturalalign) 的结果为 8 。  

3 、   面试题的解答

至此,我们可以对 Intel 、微软的面试题进行全面的解答。

程序中第 2 行 #pragma
pack (8) 虽然指定了对界为 8 ,但是由于 struct
example1 中的成员最大size 为 4 ( long 变量 size 为 4 ),故 struct
example1 仍然按 4 字节对界, struct
example1 的 size 为 8,即第 18 行的输出结果;

struct example2 中包含了 struct
example1 ,其本身包含的简单数据成员的最大 size 为 2 ( short变量 e ),但是因为其包含了 struct
example1 ,而 struct example1 中的最大成员 size 为 4 , struct
example2 也应以 4 对界, #pragma
pack (8) 中指定的对界对 struct example2 也不起作用,故 19 行的输出结果为 16 ;

由于 struct example2 中的成员以 4 为单位对界,故其 char 变量 c 后应补充 3 个空,其后才是成员 struct1 的内存空间, 20 行的输出结果为 4 。

 

  
    当未用 #pragma 指令指定编译器的对齐位数时,结构体按最长宽度的数据成员的宽度对齐;当使用了#pragma 指令指定编译器的对齐位数时,结构体按最长宽度的数据成员的宽度和
#pragma 指令指定的位数中的较小值对齐。

联合体的内存问题

45union u
{

 double a;

 int b;

}; 

 

union u2

{

 char a[13];

 int b;

}; 

 

union u3

{

 char a[13];

 char b;

}; 

 

cout<<sizeof(u)<<endl; // 8

cout<<sizeof(u2)<<endl; // 16

cout<<sizeof(u3)<<endl; // 13
 

   都知道union的大小取决于它所有的成员中,占用空间最大的一个成员的大小。所以对于u来说,大小就是最大的double类型成员a了,所以 sizeof(u)=sizeof(double)=8。但是对于u2和u3,最大的空间都是char[13]类型的数组,为什么u3的大小是13,而 u2是16呢?关键在于u2中的成员int b。由于int类型成员的存在,使u2的对齐方式变成4,也就是说,u2的大小必须在4的对界上,所以占用的空间变成了16(最接近13的对界)。 struct s1
{

 char a;

 double b;

 int c;

 char d; 

}; 

 

struct s2

{

 char a;

 char b;

 int c;

 double d;

}; 

 

cout<<sizeof(s1)<<endl; // 24

cout<<sizeof(s2)<<endl; // 16
 

   同样是两个char类型,一个int类型,一个double类型,但是因为对界问题,导致他们的大小不同。计算结构体大小可以采用元素摆放法,我举例子 说明一下:首先,CPU判断结构体的对界,根据上一节的结论,s1和s2的对界都取最大的元素类型,也就是double类型的对界8。然后开始摆放每个元 素。

 

  对于s1,首先把a放到8的对界,假定是0,此时下一个空闲的地址是1,但是下一个元素d是double类型,要放到8的对界 上,离1最接近的地址是8了,所以d被放在了8,此时下一个空闲地址变成了16,下一个元素c的对界是4,16可以满足,所以c放在了16,此时下一个空 闲地址变成了20,下一个元素d需要对界1,也正好落在对界上,所以d放在了20,结构体在地址21处结束。由于s1的大小需要是8的倍数,所以21- 23的空间被保留,s1的大小变成了24。

 

  对于s2,首先把a放到8的对界,假定是0,此时下一个空闲地址是1,下一个元素的对界也 是1,所以b摆放在1,下一个空闲地址变成了2;下一个元素c的对界是4,所以取离2最近的地址4摆放c,下一个空闲地址变成了8,下一个元素d的对界是 8,所以d摆放在8,所有元素摆放完毕,结构体在15处结束,占用总空间为16,正好是8的倍数。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  内存 编译器 面试题