您的当前位置:首页正文

内存对齐详解

来源:九壹网

内存对齐,memory alignment.为了提高程序的性能,数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;然而,对齐的内存访问仅需要一次访问。
内存对齐一般讲就是cpu access memory的效率(提高运行速度)和准确性(在一些条件下,如果没有对齐会导致数据不同步现象).依赖cpu,平台和编译器的不同.一些cpu要求较高(这句话说的不准确,但是确实依赖cpu的不同),而有些平台已经优化内存对齐问题,不同编译器的对齐模数不同.总的来说内存对齐属于编译器的问题.

一般情况下不需要理会内存对齐问题,内存对齐是编译器的事情.但碰到一些问题上还是需要理解这个概念.毕竟c/c++值直接操作内存的语言.需要理解程序在内存中的分布和运行原理.

总之一句话就是:不要让代码依赖内存对齐.

2、性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

例子:
VC++6.0中n 默认是8个字节,可以修改这个设定的对齐参数
也可以采用指令:#pragma   pack(xx)控制.

1.基础例子

 1 #include <iostream>
 2 #include <cstdio>
 3 using namespace std;
 4 #pragma   pack(n)
 5 struct A
 6 {
 7 char   c; //1byte
 8 double d; //8byte
 9 short s; //2byte
10 int i; //4byte
11 };
12 int main(int argc, char* argv[])
13 {
14 A strua;
15 printf("%len:d\n",sizeof(A));
16 printf("%d,%d,%d,%d",&strua.c,&strua.d,&strua.s,&strua.i);
17 return 0;
18 }
View Code

1)n设置为8byte时
结果:len:24,
1245032,1245040,1245048,1245052


4)当对齐参数设置为2byte时
上例结果为:Len:16
1245040,1245042,1245050,1245052
Strua.c分配后,向后找一第一个能被2整除的位置来存放strua.d,依次类推


5)1byte对齐时:
上例结果为:Len:15
1245040,1245041,1245049,1245051
此时,N=min(sizeof(成员),1),取N=1,由于1可以整除任何整数,所以各个成员依次分配,没有间空.


6)当结构体成员为数组时,并不是将整个数组当成一个成员来对待,而是将数组的每个元素当一个成员来分配,其他分配规则不变,如将上例的结构体改为:
struct A
{
char c; //1byte
double d; //8byte
short s; //2byte
char  szBuf[5];
};
对齐参数设置为8byte,则,运行结果如下:
Len:24
1245032,1245040,1245048,1245050
Strua 的s分配后,接下来分配Strua 的数组szBuf[5],这里要单独分配它的每个元素,由于是char类型,所以N=min(1,8),取N=1,所以数组szBuf[5]的元素依次分配没有间隙。

看完上述的例子,基本分配的规律和方法应该已经知道.下面主要说明数组,嵌套结构体,指针时的一些内存对齐问题.
最重要的是自己写程序证明.

 

2.数组,嵌套.
测试环境:64位 ubuntu;g++ (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3

alignment.cpp代码如下:

#include <iostream>
#include <cstdio>
using namespace std;
#pragma pack(8)
struct Args
{
        char ch; 
        double d;
        short st; 
        char rs[7];
        int i;
} args;
struct Argsa
{
        char ch; 
        Args test;
        char jd[10];
        int i;
}arga;
int main()
{
// cout <<sizeof(char)<<" "<<sizeof(double)<<" "<<sizeof(short)<<" "<<sizeof(int)<<endl;
//cout<<sizeof(long)<<" "<<sizeof(long long)<<" "<<sizeof(float)<<endl;
cout<<"Magnum1 ch =="<<(unsigned long)&args.ch<<" d=="<<(unsigned long)&args.d<<" st=="<<(unsigned long)&args.st<<" rs=="<<(unsigned long)&args.rs<<" i=="<<(unsigned long)&args.i<<endl;
cout<<"Args:"<<sizeof(args)<<endl;
cout<<""<<(unsigned long)&args.i-(unsigned long)&args.rs<<endl;

cout<<"Argsa:"<<sizeof(arga)<<endl;
cout<<"Magnum2 Argsa ch=="<<(unsigned long)&arga.ch<<" test=="<<(unsigned long)&arga.test<<" jd=="<<(unsigned long)&arga.jd<<" i=="<<(unsigned long)&arga.i<<endl;
cout<<"Argsa:"<<(unsigned long)&arga.i -(unsigned long)&arga.jd<<endl;
cout<<"Argsa:"<<(unsigned long)&arga.jd-(unsigned long)&arga.test<<endl;
return 0;
}
View Code

输出结果:

Magnum1 ch ==6295936 d==6295944 st==6295952 rs==6295954 i==6295964
Args:32
10
Argsa:56
Magnum2 Argsa ch==6295968 test==6295976 jd==6296008 i==6296020
Argsa:12
Argsa:32

struct Args长度32 struct Argsa长度:56.
改成#pragma pack (16)结果一样.
这个例子证明了三点:
对齐长度长于struct中的类型长度最长的值时,设置的对齐长度等于无用.
数组对齐的长度是按照数组成员类型长度来比对的.
嵌套的结构体中,所包含的结构体的对齐长度是结构体的对齐长度.

 

3.指针.主要是因为32位和64位机寻址上
测试环境同2.(64位系统)

关于指针和数组:

数组就是数组,其大小与元素的类型和个数有关;定义数组的时必须指定其元素的类型和个数;数组可以存任何类型的数据,但不能存函数。

指针和数组是两个完全不一样的东西。只是他们都可以“以指针的形式”或以“以下标的形式”进行访问。

 

alignment-pointer.cpp代码如下:

#include <iostream>
#include <cstdio>
#pragma pack(4)
using namespace std;
struct Args
{
        int i;
        double d;
        char *p; 
        char ch; 
        int *pi;
}args;
int main()
{    
        cout<<"args length:"<<sizeof(args)<<endl;
    cout<<"Magnum pointer i =="<<(unsigned long)&args.i<<" d=="<<(unsigned long)&args.d<<" p=="<<(unsigned long)&args.p<<" ch=="<<(unsigned long)&args.ch<<" pi=="<<(unsigned long)&args.pi<<endl;
        cout<<(unsigned long)&args.ch-(unsigned long)&args.p<<endl;
        cout<<(unsigned long)&args.pi-(unsigned long)&args.ch<<endl;
        return 0;
}
View Code

 

输出结果如下:

args length:32
Magnum pointer i ==6295936 d==6295940 p==6295948 ch==6295956 pi==6295960
8
4

这里面为什么ch==6295956,找拉很久后来发现我当前的系统是ubuntu12.04-64bits的,一个指针占用8bytes,而32bits系统不会这样。

所以这里提醒我们考虑内存对齐,要先弄清楚自己当前OS的位数

设置pack为8时:

args length:40
Magnum pointer i ==6295936 d==6295944 p==6295952 ch==6295960 pi==6295968
8
8

 

最后补充一个宏_INTSAIZEOF的作用是求出变量占用内存空间的大小,定义如下:

#define _INTSIZEOF(n)   ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )

计算出n的大小是4的倍数,4,8,12....

 

结构体对齐模数是结构体内部最大数据成员长度和pragma pack中较小者

因篇幅问题不能全部显示,请点此查看更多更全内容

Top