【寒江雪】C++内存对齐原则

来源:互联网 发布:淘宝卖店铺 编辑:程序博客网 时间:2024/05/17 03:52

C++内存对齐原则

  C++内存对齐的原则有四个,分别如下:

  1. 数据成员对齐规则:结构或联合的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小或者成员的子成员大小(只要该成员有子成员,比如说是数组,结构体等)的整数倍开始,基本类型不包括struct/class/uinon。
  2. 结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部”最宽基本类型成员”的整数倍地址开始存储.(struct a里存有struct b,b里有char,int ,double等元素,那b应该从8的整数倍开始存储.)。
  3. 收尾工作:结构体的总大小,也就是sizeof的结果,.必须是其内部最大成员的”最宽基本类型成员”的整数倍.不足的要补齐.(基本类型)。
  4. sizeof(union),以结构里面size最大元素为union的size,因为在某一时刻,union只有一个成员真正存储于该地址。


  以上四条原则。可以说是清晰明了了。总的来说,C++的内存分配,我们从对结构体的内存分配来分析。
  首先,无论是结构体的嵌套还是不嵌套,我们都以其中占内存最大的基本类型为基准来申请内存块。这是上边第二条原则描述的内容.
  第二,分配第一个内存块,将第一个数据成员放在offset为0的地方,第二个数据成员的位置,放在第一个结束地址之后首个能整除第二个数据成员大小的地址(地址是整数,数据成员也是整数,前者能整除后者的时候,该地址就是符合的。)。但是这样会有一个意外,那就是剩余内存块中都没有能够整除该数据大小的地址了,这种情况就会申请新的内存块,然后存放该类型。(这里指的都是基本类型)。这样的操作会一直按深度优先搜索顺序递归,直到分配完毕。
  第三,如果最后一个内存块没有用完,那么冗余就会一直存在。这是时空平衡的结果。

  对于联合体,联合体里边有多个基本类型的时候肯定是满足前三条的,如果该联合体不是嵌套结构,那么它的大小就是最大的那个基本类型的大小.但是我们就有个疑问了,如果有结构嵌套的时候,改怎么分配内存空间。
  对于联合体,首先它的内存分配肯定和结构体类似的,它的内存分配过程,它只写在最上层的结构共享内存空间,而每一种结构内存分配的过程都与结构体内存分配的过程一致。它申请内存块的大小,是以所有结构中最大的基本类型的大小为准的,主要区别就是,每申请完一种类型的空间时,它就从首地址重新给下一个类型分配空间。直到所有类型都分配完为止,最终得到的空间大小就是该联合体的大小了。
  联合体的内容可以参考下边这段代码:

#include<iostream>#include<cassert>#include<string>using namespace std;void PrintAddress(string name,void* x){    cout << name;    cout << hex << x << endl;}struct TEST_A{    char _char;    int _int;};struct TEST_J_I{    char _char[5];    int _int;};struct TEST_J_II{    char _char[5];    TEST_J_I test_j_i;};struct TEST_J{    char _char;    double _double;    TEST_J_II test_j_ii;};union UNION_C{    char _char[3];    TEST_J _testj;    TEST_A _testa;};int main(){UNION_C unionc;    PrintAddress("UNION_C::_char[3]:", &unionc._char);    PrintAddress("UNION_C::TEST_J::_char:", &unionc._testj._char);    PrintAddress("UNION_C::TEST_J::_double:", &unionc._testj._double);    PrintAddress("UNION_C::TEST_J::TEST_J_II::_char[5]:", &unionc._testj.test_j_ii._char);    PrintAddress("UNION_C::TEST_J::TEST_J_II::TEST_J_I::_char[5]:", &unionc._testj.test_j_ii.test_j_i._char);    PrintAddress("UNION_C::TEST_J::TEST_J_II::TEST_J_I::_int:", &unionc._testj.test_j_ii.test_j_i._int);    PrintAddress("UNION_C::TEST_A::_char:", &unionc._testa._char);    PrintAddress("UNION_C::TEST_A::_int:", &unionc._testa._int);    //输出结果    //SIZEOF UNION_C : 40    //UNION_C::_char[3] : 004FFD1C    //UNION_C::TEST_J::_char : 004FFD1C    //UNION_C::TEST_J::_double : 004FFD24    //UNION_C::TEST_J::TEST_J_II::_char[5] : 004FFD2C    //UNION_C::TEST_J::TEST_J_II::TEST_J_I::_char[5] : 004FFD34    //UNION_C::TEST_J::TEST_J_II::TEST_J_I::_int : 004FFD3C    //UNION_C::TEST_A::_char : 004FFD1C    //UNION_C::TEST_A::_int : 004FFD20    return 0;}

  UNION_C中,最上层的类型是char,TEST_J,TEST_A,因此,他们是独立的,先分配char,再从头分配TEST_J,再从头分配TEST_A.
  今天我对根据所有这四条原则都做了实验.并且做成了EXCEL表格,但是表格上没有标注,也没用注明是哪一组,其中第J组实验修改数据并重新运行有5~10次左右,最后得到的结论很好,读者可以自己看看我的代码,根据这四条原则来分析和探索一下。我会把代码传到Github上。


Copyright© by 寒江雪1719
Date:2017.8.12
Github:https://github.com/lkysyzxz/BasicReview