还是自己记录一下吧,因为没有被字节对齐坑过可能每次看了就忘记了。每次面试的时候还总是会有,我每次都会有些模糊。还是眼过千遍不如手过一遍,今天看到一个微软的面试题关于字节对齐,自以为记得内存对齐,自己算了一遍,然后一看下面答案就发现了,自己记错了。虽然记得大概却总是会混乱,我还是的记录一下。废话不说了。上菜。
今天看到的微软面试题:
好习惯标记出处来自博客:https://blog.csdn.net/dreamback1987/article/details/8504943
#include <iostream.h>
#pragma pack(8)
struct example1
{
short a;
long b;
};
struct example2
{
char c;
example1 struct1;
short e;
};
#pragma pack()
int main(int argc, char* argv[])
{
example2 struct2;
cout << sizeof(example1) << endl;
cout << sizeof(example2) << endl;
cout << (unsigned int)(&struct2.struct1) - (unsigned int)(&struct2) << endl;
return 0;
}
问程序的输入结果是什么?
答案是:
8
16
4
我的错误答案:8、12、2。
可能因为我之前对字节对齐有误会哈哈。
我抱着不相信我自己记错了的态度,我又在自己电脑上测试了一番,代码如下:
//
// main.cpp
// test_memory_alignment
//
// Created by Wtayu on 2019/7/29.
// Copyright © 2019 Wtayu. All rights reserved.
//
#include <iostream>
struct A{
char str;
int num;
short s_num;
};
struct Intel{
char c;
short a;
long b;
short e;
};
struct example1{
short a;
int b;
};
struct example2
{
char c;
example1 struct1;
short e;
};
int main(int argc, const char * argv[]) {
//测试1
std::cout<<"测试1:"<<std::endl;
A a[2];
std::cout <<"sizeof(a):" <<sizeof(a)<<std::endl;
std::cout <<"struct a0:" << &a <<std::endl;
std::cout <<"str:"<<(void *)&a[0].str<<std::endl;
std::cout <<"num:" <<&a[0].num<<std::endl;
std::cout <<"s_num:"<<&a[0].s_num<<std::endl;
std::cout <<"a1:"<<&a[1]<<std::endl;
//测试2
Intel i[2];
std::cout<<"测试2:"<<std::endl;
std::cout <<"sizeof(Intel):" <<sizeof(Intel)<<std::endl;
std::cout <<"struct i0:" << &i <<std::endl;
std::cout <<"c:"<<(void *)&i[0].c<<std::endl;
std::cout <<"a:" <<&i[0].a<<std::endl;
std::cout <<"b:"<<&i[0].b<<std::endl;
std::cout <<"e:"<<&i[0].e<<std::endl;
std::cout <<"i1:"<<&i[1]<<std::endl;
//测试3
example2 e[2];
std::cout<<"测试3:"<<std::endl;
std::cout <<"sizeof(example2):" <<sizeof(example2)<<std::endl;
std::cout <<"struct e0:" << &e <<std::endl;
std::cout <<"e.cc:"<<(void *)&e[0].c<<std::endl;
std::cout <<"e.struct1:" <<&e[0].struct1<<std::endl;
std::cout <<"e.e:"<<&e[0].e<<std::endl;
std::cout <<"e1:"<<&e[1]<<std::endl;
return 0;
}
输出如下:
测试1:
sizeof(a):24
struct a0:0x7ffeefbff4d0
str:0x7ffeefbff4d0
num:0x7ffeefbff4d4
s_num:0x7ffeefbff4d8
a1:0x7ffeefbff4dc
测试2:
sizeof(Intel):24
struct i0:0x7ffeefbff4a0
c:0x7ffeefbff4a0
a:0x7ffeefbff4a2
b:0x7ffeefbff4a8
e:0x7ffeefbff4b0
i1:0x7ffeefbff4b8
测试3:
sizeof(example2):16
struct e0:0x7ffeefbff480
e.cc:0x7ffeefbff480
e.struct1:0x7ffeefbff484
e.e:0x7ffeefbff48c
e1:0x7ffeefbff490
Program ended with exit code: 0
发现人家是对的,我是错的。
上面大那个链接里也谈到了,自然对齐(natural alignment)ps:他翻译的是自然对界。
我自己再总结一番吧:
自然对齐:指的是编译器的默认对齐规则。对齐规则简单分为两个影响因素:1、下一个变量“大小”。2、一般就是类内“最大”的变量;一条一条来说:
针对第一条可以理解为:下一个变量的大小如果大于当前的变量。则需要给当前变量补齐。例:
char c;
int a;
当前变量为c,大小为1字节,而下一个变量大小则为4,即符合了“下一个变量的大小如果大于当前的变量”这句话。则需要给c补齐3位,需要和a一样长。
第二条则可以理解为,默认对齐规则下类的字节大小一定是类内“最大”变量字节数的倍数。
对于每个成员都符合第一条,然后判断第二个条,如果第二条不符合则在最后变量后补齐倍数所差字节。例:
char c1;
int l;
char c2;
根据第一条算的字节数为:1+补位3+4+1=9,再根据第二条,9不是最大变量4字节的倍数,补齐3位,则为12字节。
分割线
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------
有了上面的知识其实已经可以做微软那个面试题了,但是微软那个题还有个奇怪的地方就是example2内有example1的成员变量。这个其实是需要稍微修改一下第一条原则即可:下一个变量的对齐规则如果大于当前变量的对齐规则。则需要按照下一个对齐规则给当前变量补齐。这样只需要思考example1的对齐规则是多少自己,其实就是最大成员字节数4。
好了这样就可以算出来正确的答案了。
分割线
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------
那个面试题还有一个问题:
#pragma pack(8)
#pragma pack()
这个是修改自然对齐规则,也就是定义对齐规则。第一行是将对齐规则改为8,第二行则是改为默认。
第一行改为8则是将之前提到的所有补齐的标准换为8字节。这里我就不多说了,不懂就自己写demo测试吧。
写这个一为记录、二为帮助大家理解。