数据的对齐

数据的对齐

cpu在读写内存的时候,如果所给的地址是机器字长的整数倍,则操作效率会比较高,这可以称之为地址的对齐。

在一般的32bits机器上,地址对齐的界线默认是4的整数倍。

struct my_struct{
    char ch1;  //1字节
    char ch2; //1字节
    int n; //4字节
    char ch3; 1字节
};//整个结构体在32bits系统占12个字节

对结构体类型采用sizeof操作符,得到的是结构体占用的内存字节数,包括所有的空闲字节,显然,这个值并不一定等于它的所有成员的大小之和。

#pragma pack(1) //将地址对齐界线改为1的整数
struct my_struct{
    char ch1;  //1字节
    char ch2; //1字节
    int n; //4字节
    char ch3; 1字节
};//整个结构体占7个字节
#pragma pack() //将地址对齐界线改回原来的值

对齐规则参考

该博文也给出了#pragma pack(show) 和#pragma pack(num)的说明以及在VS中的设置;默认num=8.

这内存宝贵的应用场合,比如嵌入式系统,一般要考虑对齐,一般原则,可以是从小到大排,把多个小字节变量放一起,并且跟对齐的大小对齐。

一般来说,

  • 结构体的对齐规则是先按数据类型自身进行对齐,这里需要比较#pragma pack(num)中num的数值和数据类型的大小,取小的作为基准;
  • 然后再按整个结构体进行对齐,对齐值必须是2的幂,比如1,2, 4, 8, 16。准确来讲,结构的总大小是其成员中最大类型的sizeof(该类型)整数倍。
  • 如果一个类型按n字节对齐,那么该类型的变量起始地址必须是n的倍数。比如int按四字节对齐,那么int类型的变量起始地址一定是4的倍数,比如0x0012ff60,0x0012ff48等。

查看系统是多少位的

file /sbin/init

/sbin/init: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=7a4c688d009fc1f06ffc692f5f42ab09e68582b2, stripped

file /bin/ls

/bin/ls: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=3bf9ca54ea5e261943509c2e47bc814bb1248921, stripped

uname -a

Linux ubuntu 3.13.0-79-generic #123-Ubuntu SMP Fri Feb 19 14:27:58 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux

cat /proc/version

Linux version 3.13.0-79-generic (buildd@lcy01-24) (gcc version 4.8.2 (Ubuntu 4.8.2-19ubuntu1) ) #123-Ubuntu SMP Fri Feb 19 14:27:58 UTC 2016

cat /etc/issue

Ubuntu 15.04 \n \l

测试数据所占字节

因为在数据对齐的时候需要考虑是在什么系统下,各数据类型所占字节大小,纠正网上一些有问题的解答,比如long是只占4bits的。

cout << "char: " << sizeof(char) << endl;
cout << "short: " << sizeof(short) << endl;
cout << "int: " << sizeof(int) << endl;	
cout << "long: " << sizeof(long) << endl;
cout << "unsigned long: " << sizeof(unsigned long) << endl;
cout << "long long: " << sizeof(long long) << endl;
//在vc6.0下不支持 long long 数据类型。
cout << "double: " << sizeof(double) << endl;
cout << "char*: " << sizeof(char*) << endl;

32bit windows xp (VC6.0 IDE 32bits) 测试:

char: 1
short: 2
int: 4
long: 4
unsigned long: 4
double: 8
char*: 4

64bit windows 10 (Dev-C++ 5.11 64bits的)测试:

Window系统下的MinGW,总是编译为32位代码。因为MinGW只支持32位代码。

char: 1
short: 2
int: 4
long: 4             // 不知道为什指针又是8字节的!!!
unsigned long: 4
long long: 8
double: 8   
char*: 8            //指针所占内存大小有区别,每字节8位,跟总线搭配

64bit ubuntu15.04 (g++ 4.8.4)测试:

char: 1
short: 2
int: 4
long: 8		// 8 !!
unsigned long: 8
long long: 8
double: 8
char*: 8    //指针所占内存大小有区别,每字节8位,跟总线搭配

地址对齐的概念:

  • 由于硬件设计的特点,CPU在读写内存时,如果所给的地址是机器字长的整数倍,则操作效率会比较高。
  • 有的CPU甚至不支持读写地址不对齐的内存单元。
  • 为了提高程序运行的效率,编译器会尽量避免一个变量(包括结构体成员)的存储空间跨越地址对齐的界线。
  1. 实验(64bits 环境)

     #include <iostream>
     using namespace std;
     typedef struct{	
     
     }test_null;      	 // test 1
    
     typedef struct{
     	double d1;	//8
     	char c1;	//1
     	short s1;	//补1+2
     	char ch2;	//1
     	int i1;		//补3+4+补4  => 24
     }testdouble;        // test 2   
     
     typedef struct{
     	int d1;		//4
     	char c1;	//1
     	short s1;	//fill 1 + 2
     	char ch2;	//1
     	int i1;		//fill 3 + 4  => 16
     }testint;        	// test 3
     
     #pragma pack(2)
     typedef struct{
     	int d1;		//4
     	char c1;	//1
     	short s1;	//fill 1 +2
     	char ch2;	//1
     	int i1;		//fill 1 + 4  => 14
     }testint2; 
    
     class BU{
          int number;				//4
          union UBffer{
              char buffer[13];//13
              int number;   	//4
          }ubuf;						//->13	
          void foo(){}				//0
          typedef char*(*f)(void*);	//0
          enum{hdd,ssd,blueray}disk;	//fill 1 + 4
          int* a;					//8 		=> 30
     }bu;       			// test 4
     #pragma(4)
     
     #pragma pack(1) //
      typedef struct {
     	char ch1;	//1
     	char ch2;	//1
     	int n;		//4
     	char ch3;	//1
     }stch3_1; 	// -> 7
     
     class clt_1{
     	char ch1;	//1
     	int a;		//4
     	short b;	//2
     	char c;		//1
     	stch3_1 st1;//7 => 15
     }st_inside;       	// test 5
     #pragma pack(4) //
    

输出控制:

  • 64bits win10(GCC 4.9.2)测试结果:

      test null struct: 1
      sizeof(bu): 30
      sizeof(testdouble):24
      sizeof(testint):16
      sizeof(testint2):14
      st_inside: 15,  inside stch3_1: 7
    
  • 64bits ubuntu (gcc version 4.8.4 ) 测试结果:

      test null struct: 1
      sizeof(bu): 30
      sizeof(testdouble):24
      sizeof(testint):16
      sizeof(testint2):14
      st_inside: 15,  inside stch3_1: 7
    

注意:

  • 空的结构体在linux系统中并不是0,它和windows 一样,均是占一个字节;
  • long类型在32bits 是占4个字节,64bits 编译器中是8字节;
  • 在32bits 系统中指针是4字节,64bits系统占8字节,由于总线物理跟指针的空间是一样大的:4*8 -> 32 ; 8*8 -> 64 .

posted on 2016-08-30 18:50  xweel  阅读(340)  评论(0)    收藏  举报

导航