有趣的内存对齐面试题
题目描述:
内存对齐机制是一个用空间换取时间的做法,具体怎么做的,或者为什么要这么做?可以参考我的博客:内存对齐
这道题是这样的:问程序会在那么地方崩溃?
如果你直接答:那么你就错了兄弟。这道题是分平台的。我们先在Linux CentOS X86_64位平台下运行一下试试看。
#include <iostream>
struct S
{
int a;
int *p;
};
int main(void)
{
struct S s;
int *p = &s.a;
p[0] = 1;
p[1] = 2;
s.p = p;
s.p[1] = 1;
s.p[0] = 2;
std::cout << "a : " << s.a << " p : " << s.p << std::endl;
return 0;
}
程序并没有崩溃呀?
但是!如果把结构体的int
改为long
再看!
#include <iostream>
struct S
{
long a;
long *p;
};
int main(void)
{
struct S s;
long *p = &s.a;
p[0] = 1;
p[1] = 2;
s.p = p;
s.p[1] = 1;
s.p[0] = 2;
std::cout << "a : " << s.a << " p : " << s.p << std::endl;
return 0;
}
这是为什么呢??
再将这个原因之前,我们来看一下在CentOS x86_64位平台下,这些变量的大小分别是多少。
std::cout << "int : " << sizeof(int) << std::endl;
std::cout << "int* : " << sizeof(int *) << std::endl;
std::cout << "long : " << sizeof(long) << std::endl;
std::cout << "long* : " << sizeof(long*) << std::endl;
题目解释:
为了方便起见,我们将程序移到VS下,因为VS可以配置x86 和 x64切换。实现双平台。
32位平台下
因为在32位平台下,int
和int*
都是4个字节,所以他们在内存中的存储是这样的。
我们通过编译器的监视窗口也可以看到。
所以当程序运行时:
s.p[1] 其实就等于s.p。那个指针赋值就已经把这个结构体的首地址赋给s.p了,s.p[1]就相当于从结构体s的首地址开始向后走4个字节,那不就是s.p嘛。然后s.p[1] = 1,就相当于把自己的地址改成1了。然后在s.p[0]这个解引用操作的时候无法给1这个地址解引用,所以发生段错误。
64位平台下
但是在64位平台下,int
为4个字节,int*
为8个字节。所以他在内存中的存储是这样的。
监视窗口同样可以体现:
两个地址隔了8个字节,但是a只占4个字节,所以,p对齐到偏移量为8的地址处了。
那么我们再看刚才的程序:
当程序运行到这里的时候:(看内存窗口)
s.[1]只是改的是,s.a后面填充的4个字节,并没有改s.p,所以当然不会发生段错误呀。
小结:所以这个面试题还是很坑的。
你要想清楚,是不是不同平台下结果不一样。所以在面试的时候,还是要多考虑考虑不同的情况。
叮~上菜!