【sizeof()求内存、内存对齐】

一、内存对齐的概念和作用

1、概念

内存对齐是指按照特定的规则来组织数据在内存中的排列方式,以提高内存访问的效率。

2、作用

  1. 硬件效率:许多CPU在硬件层面对内存访问进行了优化,它们可以从特定对齐的内存地址更高效地读取数据。未对齐的内存访问可能会导致性能下降,甚至在架构上导致异常;
  2. 兼容性:内存对齐是许多系统硬件的要求。不正确的对齐可能会导致未定义行为,包括程序崩溃或数据损坏。C++通过内存对齐确保了跨平台兼容性和程序的可移植性;
  3. 内存访问优化:内存对齐可以减少CPU访问内存时所需的·总周期数。这是因为对齐的数据可以直接被硬件高效地读取,而不需要额外的内存访问操作;
  4. 缓存利用:内存系统通常以缓存行(cache line)为单位存储数据。内存对齐可以提高缓存的利用率,因为对齐的数据更有可能被完整地存储在单个缓存行中;
  5. 多平台支持:不同的硬件平台可能有不同的对齐方式。C++的内存对齐特性允许开发者编写能够在多种架构上运行而无需修改的代码;
  6. 接口与ABI(应用二进制接口):内存对齐对于维护与C语言及其他语言的兼容性至关重要。C++类和结构体的内存分布需要与C语言结构体保持一致,以确保可以轻松地与C语言进行交互;
  7. 避免字节填充:在没有明确对齐要求的情况下,编译器可能会在结构体中插入额外的字节填充(padding),以满足成员的对齐要求。这可能导致不必要的内存浪费。显示内存对齐可以减少这种填充,节省内存空间;
  8. 模板元编程:在模板编程中,特别是在设计复杂数据结构时,显示控制内存对齐可以避免不必要的内存浪费,并提高程序性能;
  9. 安全性:内存对齐有助于避免未定义行为。通过确保数据按照特定的规则对齐,C++可以减少因内存访问错误导致的安全问题;
    10.语言特性的完善:内存对齐是C++追求成为一个更全面、高效和安全的语言的一部分。C++11及后续标准引入了alignas(用于指定变量或类型的内存对齐字节数)和alignof(用于查询变量或类型的内存对齐字节数)等特性,以提供一种标准的方式来处理内存对齐问题。

二、内存对齐的基本原则

  1. 结构(struct/class)的内置类型数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员的起始位置要从自身大小的整数倍开始存储(注意:64位机器的指针大小为8个字节);
  2. 如果一个结构体A里有结构体成员B,则结构体B要从其内部“最宽基本类型成员”的整数倍地址开始存储(如strcut a中存有struct b,b里有char ,int,double等类型,那么b就应该从double的字节数8的整数倍位置开始存储);
  3. 结构体的总大小为结构体有效对齐值的整数倍,结构体的有效对齐值的确定:
    (1)当未明确指定时,以结构体或结构体所包含结构体成员中最长的成员长度为其有效值;
    (2)当用#pragma pack(n)指定时,以n和结构体中最长的成员的长度较小值为其有效值:
    (3)当用_attribute_((packed))指定长度时,强制按照此值为结构体的有效对齐值;
    (4)不管#pragma pack和_attribute_((packed))如何指定,结构体内部成员的自对齐仍然是按照其自身的对齐值。

注意:一个空类的占用内存为1个字节而非0!

三、常见类型数据成员的内存对齐

1、空类/静态成员

class A{};

int main(){
    cout<<sizeof(A)<<endl;//空类内存占用字节数输出为1
    return 0;
}
class A{
    static int x;
};

int mian(){
    cout<<sizeof(A)<<endl;//静态成员内存占用字节数输出为1
}

2、内置类型数据成员

class Data{
    char c;
    int a;
};
 int mian(){
    cout<<sizeof(Data)<<endl;//8
}

因为sizeof(char) = 1,sizeof(int) = 4,按照最长的字节数来进行存储,所以1 + (3)+ 4 = 8。

class Data{
    char a;
    double b;
};

int mian(){
    cout<<sizeof(Data)<<endl;//16
}

因为sizeof(char) = 1,sizeof(double) = 8,按照最长的字节数来进行存储,所以1 + (7)+ 8 = 16。

class Data
{
    char c;
    int a;
    char d;
};
 
cout << sizeof(Data) << endl;//12
class Data
{
    char c;
    char d;
    int a;
};
 
cout << sizeof(Data) << endl;//8

3、结构体数据成员

class BigData
{
    char array[33];
};
 
class Data
{
    BigData bd;
    int integer;
    double d;
};
 
cout << sizeof(BigData) << "   " << sizeof(Data) << endl;//33 48
class BigData
{
    char array[33];
};
 
class Data
{
    BigData bd;
    double d;
};
 
cout << sizeof(BigData) << "   " << sizeof(Data) << endl;//33 48
 class A {                                                                                         
 public:                                                                                           
     double len;                                                                                   
     char str[33];                                                                                 
 };                                                                                                
                                                                                                   
 class B {                                                                                         
 public:                                                                                                                                          
     A a;                                                                                          
     int b;                                                                                        
 };

cout << sizeof(A) << "  " << sizeof(B) << endl;//48 56

4、虚函数

C++类中如果有虚函数,类内就会有一个虚表指针_vfptr,指向自己的虚函数表,vptr一般都是在类的最前面。

class A {
public:
	A(){}
	virtual ~A(){}
	virtual void foo(){}
	virtual void print() {}
};
cout<<sizeof(A)<<endl;//32位下指针的字节数为4,64位下指针式字节数为8

注意:
对于没有override的虚函数,基类和派生类中_vfptr指向的虚函数表中,这个虚函数的地址是一样的,而对于重写了的或者默认重写的析构函数来说,_vfptr指向的虚函数表,函数地址是不一样的(当然两个类的虚表指针指向的地址也是不一样的)。

5、继承

不同的编译器对继承后的类的大小的计算方式不同,有的是先继承后对齐,有的是先对齐后继承:

class A
{
    int i;
    char c1;
}

class B:public A
{
    char c2;
}

class C:public B
{
    char c3;
}

gcc下,sizeof© = 8(先继承,后对齐)
vs下,sizeof© = 16(先对齐,后继承)

  • 32
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值