C/C++ 数据对齐知识摘要

#include <iostream>   
using std::cout;   
using std::endl;   
#include <iomanip>   
using std::setw;   
using std::left;   
// 数据对齐   
// 环境: windows 2003 + Intel Celeron CPU 2.53GHz + VC6.0   
// 资源参考:《Linux 内核设计与实现 第二版》ISBN:7-111-17865-3/TP.4549   
void sizeofClass();   
  
void main()   
{   
 cout <<left<<setw(20)<<"sizeof(bool)"  
  <<sizeof(bool)<<endl;    // 1   
 cout <<left<<setw(20)<<"sizeof(char)"  
  <<sizeof(char)<<endl;    // 1   
 cout <<left<<setw(20)<<"sizeof(short)"  
  <<sizeof(short)<<endl;    // 2   
 cout <<left<<setw(20)<<"sizeof(int)"  
  <<sizeof(int)<<endl;    // 4   
 cout <<left<<setw(20)<<"sizeof(long)"  
  <<sizeof(long)<<endl;    // 4   
 cout <<left<<setw(20)<<"sizeof(float)"  
  <<sizeof(float)<<endl;    // 4   
 cout <<left<<setw(20)<<"sizeof(double)"  
  <<sizeof(double)<<endl;    // 8   
 //cout <<setw(20)<<"sizeof(long long)"<<sizeof(long long)<<endl;   
 cout <<left<<setw(20)<<"sizeof(long double)"  
  <<sizeof(long double)<<endl;  // 8   
 cout <<left<<setw(20)<<"sizeof(void *)"  
  <<sizeof(void *)<<endl;    // 4  (指针在32位系统上占4个字节)   
    
 struct AAA{   
  double aDouble;  /* 8 bytes */  
  int aInt;   /* 4 bytes */  
  char aChar;   /* 1 byte  */  
 };   
 struct BBB{   
  char aChar;   
  double aDouble;   
  int aInt;   
 };   
    
 cout << sizeof(AAA)<<"  "<<sizeof(BBB)<<endl; // 打印 多少 ???   
  
 //sizeofClass(); //测试类的内存表示, 感兴趣的可以打开看看结果   
}    
/*     
数据对齐  

 

自然对齐:如果一个变量的内存地址正好是它长度的整倍数,它就被称为自然对齐。  
    一些体系结构对对齐要求非常严格。通常想基于RISC的系统, 载入未对齐数据会导  
致处理器陷入(一种可处理的错误)。还有一些系统可以访问没有对齐的数据,只不过性  
能会下降。编写可移植性高的代码要避免对齐问题,保证所有的类型都能够自然对齐。  

避免对齐引发的问题  

    一个数据类型长度比较小,它本来是对齐的,如果你用一个指针进行类型转换,并且  
转换后的数据类型较长,那么通过改指针进行数据访问时就会引发对齐问题。也就是说,  
下面的代码是错误的:  
 char dob[10];  
 char *p = &dog[1];  
 unsigned long l = *(unsigned long *)p;  
    这个例子将一个指向char型的指针当作指向unsigned long型的指针来用,这会引起问  
题,因为此时会试图从一个并不能被4整除的内存地址上载入32的unsigned long型数据。  

非标准类型的对齐  
      
    前面提到了,对于标准数据类型来说,它的地址只要是其长度的整数倍就对齐了。而非  
标准的(复合的)C数据类型按照下列原则对齐:  
 1〉对于数组,只要按照基本数据类型进行对齐就可以了(其后的所有元素自然都能够对  
齐了)。  
 2〉对于联合,只要它包含的长度最大的数据类型能够对齐就可以了。  
 3〉对于结构体,只要它包含的长度最大的数据类型能够对齐就可以了。  
  结构体还要引入  填补机制,这会引发下一个问题。  

结构体填补  

 为了保证结构体中每一个成员都能够自然对齐,结构体需要被填补。举例:  
 上面的代码中对于结构体 BBB 的定义如下:  

    struct BBB{  
  char aChar;  // 1 byte  
  double aDouble; // 8 bytes  
  int aInt;  // 4 bytes  
 };  

 由于该结构体不能准确地满足各个成员自然对齐,所以它在内存中可不是按照原样存放  
的。编译器会在内存中创建一个类似下面的给出的结构体:  

 struct BBB{  
  char aChar;  // 1 byte  
  u8 __pad0[7]; // 7 bytes  
  double aDouble; // 8 bytes  
  int aInt;  // 4 bytes  
  u8 __pad1[4]; // 4 bytes  
 };  

 填补的变量都是为了让数据自然对齐而加入的。__pad0 是为了让 aDouble 能够自然对  
齐而加入的,而其他额外的填补如 __pad1 则是为了让结构体的长度能够被最大元素(aDouble)  
的长度 8 整除而加入的。结构体AAA的长度为16,而不是13就是因为后一种填补引起的。  
 通常你可以通过重新排列结构中的对象来避免填充或减少填充。像结构体AAA那样,把  
元素按长度的大小递减/增的排列后就可以使它占最小的空间了。  
 注意:编译器,优化器并不能改变结构体中元素的排列次序。如ANSI C就明确规定不允  
许编译器改变结构体内成员对象的次序,它总是由你--程序员来决定。  
 并不是所有的结构体进行这样的调整的,比如:该结构体作为一个标准的一部分,或者  
它是现有代码的一部分。  

*/  
  
  
  
////////////// 以下是解释C++中类的内存表示机制   
  
void sizeofClass(){   
  
 class Father{   
 private:    
  double aDouble;  /* 8 bytes */  
  int aInt;   /* 4 bytes */  
 public:    
  char aChar;   /* 1 byte  */  
 // 为了print函数指针能够自然对齐 这里自动填补 3 个字节    
  virtual void print()const=0; // 4 bytes 函数指针   
  int getInt(){     // 4 bytes 函数指针   
   return aInt;   
  }   
 };   
  
 //虽然Son_0没有声明成员变量,但是所占大小仍然为 24 = 16 + 4 +4   
 //是因为父类的成员变量都在子类中保留,但是根据权限   
 //描述符而确定是否可以访问,父类中相应的成员函数也在子类中保留,   
 //若有新的实现则指向新的代码段,访问权限仍有权限描述符确定。   
 class Son_0:public Father{   
  //这里仍然保留父类的数据段,故占 8 + 4 + 1 + 3 = 16 bytes   
 public:   
  //这里仍然保留着父类 getInt 函数指针,故占4个字节,若被重新实现则指向不同代码段   
  double getDouble(){ // 4 bytes 函数指针   
   return (double)aChar;   
  }   
 };   
  
 /* 虽然Son_1public继承了Father的数据成员,但仍然可以再声明一个  
 public char aChar,因为保留的空间位置不一样。   
 */  
 class Son_1:public Father{   
 public:   
  char aChar;   // 1 byte   
  //为了aDouble能自然对齐,这里自动填补 7 个字节   
 private:   
  double aDouble;  // 8 bytes   
  int aInt;   // 4 bytes   
  //为了print函数指针能自然对齐,这里自动填补 4 个字节   
 public:    
  void print(){  // 4 bytes 函数指针   
   cout <<aChar<<endl;   
  }   
 };   
  
 cout << sizeof(Father) <<"  "<< sizeof(Son_0)<<"  "<< sizeof(Son_1)<<endl;   
  
  
}  

你可能感兴趣的:(struct,Class,float,byte,iostream,编译器)