请说出以下2种方式,哪一种更好,为何? ios
方式一: 程序员
void foo(int a, float b, char* ch, double d, float f);方式二:
struct A { int a; float b; char ch[5]; double d; float f; }; void foo(A* pa);
咋一看,不知道这题想要考什么,无从下手。其实该题是检查考生对于内存对齐的理解。下面咱们先看看关于内存的一些知识。 面试
什么是字节对齐,为何要对齐? 编程
现代计算机中内存空间都是按照byte划分的,从理论上讲彷佛对任何类型的变量的访问能够从任何地址开始,但实际状况是在访问特定类型变量的时候常常在特定的内存地址访问,这就须要各类类型数据按照必定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。 架构
对齐的做用和缘由:各个硬件平台对存储空间的处理上有很大的不一样。一些平台对某些特定类型的数据只能从某些特定地址开始存取。好比有些架构的CPU在访问一个没有进行对齐的变量的时候会发生错误,那么在这种架构下编程必须保证字节对齐.其余平台可能没有这种状况,可是最多见的是若是不按照适合其平台要求对数据存放进行对齐,会在存取效率上带来损失。好比有些平台每次读都是从偶地址开始,若是一个int型(假设为32位系统)若是存放在偶地址开始的地方,那么一个读周期就能够读出这32bit,而若是存放在奇地址开始的地方,就须要2个读周期,并对两次读出的结果的高低字节进行拼凑才能获得该32bit数据。显然在读取效率上降低不少。 大数据
对齐规则 spa
每一个特定平台上的编译器都有本身的默认“对齐系数”(也叫对齐模数,32位gcc 4.7上默认为8,32位VS2010上默认为8)。程序员能够经过预编译命令#pragma pack(n),n=1,2,4,8,16来改变这一系数,其中的n就是你要指定的“对齐系数”。 code
规则: 对象
一、数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,之后每一个数据成员的对齐按照#pragma pack指定的数值和这个数据成员 内存
自身长度中,比较小的那个进行。
二、结构(或联合)的总体对齐规则:在数据成员完成各自对齐以后,结构(或联合)自己也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。
三、结合一、2可推断:当#pragma pack的n值等于或超过全部数据成员长度的时候,这个n值的大小将不产生任何效果。
笔者总结:当用sizeof求结构的大小时,第一个数据大小始终为该数据的sizeof大小(即偏移位始终为0),与对齐系数无关。下一个数据的仍为其sizeof的大小,但偏移位为min(数据的sizeof大小,对齐系数)。最后对结构自己进行对齐,即结构的sizeof大小为min(结构中最大数据成员长度,对齐系数)的整数倍。注意该规则不适用于有设置位域的结构。
实例1(32位GCC 4.7):
#include <iostream> #include <cstddef> using namespace std; //#pragma pack(push, 4) struct A { int a; float b; char c; double d; int *pa; char *pc; }; //#pragma pack(pop) int main() { cout << offsetof(A, a) << endl; cout << offsetof(A, b) << endl; cout << offsetof(A, c) << endl; cout << offsetof(A, d) << endl; cout << offsetof(A, pa) << endl; cout << offsetof(A, pc) << endl; cout << sizeof(A) << endl; }输出结果:
0 4 8 16 24 28 32
分析结果:这里默认对齐系数为8,读者能够根据上面的规则推算一下,sizeof(A) = 4(a) + 4(b) + 1(c) + 7(偏移位) + 8(d) + 4(pa) + 4(pc) = 32。注意,这里别忘告终构自己的对齐,上面各个数据成员偏移后的大小恰好是对齐系数8的整数倍,就不须要再进行偏移了。若是将上面的注释去掉,即将默认对齐系数改成4,想一想sizeof(A)的大小?sizeof(A) = 4(a) + 4(b) + 1(c) + 4(偏移位) + 8(d) + 4(pa) + 4(pc) = 28。
这里的offsetof是查看数据成员在结构中的偏移位,定义在cstddef文件中。当你#pragma pack(push,n)改变默认对象系数时,n只能取一、二、四、八、16,将你想要进行内存对齐的结构放入#pragma pack(push, n) ... #pragma pack(pop)之间,这样能够防止将其余的结构也进行内存对齐。
如今回到上面的面试题,方式一调用不存在内存对齐的问题,传入的数据大小就是其原本的数据大小,而方式二就不同了,因为内存对齐的缘由,将致使传入的结构大小为32(32位GCC 4.7)个字节,sizeof(A) = 4(a) + 4(b) + 5(ch) + 3(偏移) + 8(d) + 4 + 4(结构自己对齐偏移) = 32。显然方式一的效果比较高。