C++ 内存对齐

简介:

注:本文代码测试环境为win7 X64 cpu, 编译器为gcc4.7.1 和 vs2010

 

内存对齐是编译器为了便于CPU快速访问而采用的一项技术

我们先从一个例子开始,对下面的类(或者结构体)

class node

{

char c;

int i;

short s;

}no;

sizeof(no)的值是多少呢,如果你的回答是7(1+4+2),那么你应该认真阅读下面的内容。可以在编译器上试试,输出的结果是12,这就是内存对齐的结果。

 

为什么要进行内存对齐呢?

  1. 平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
  2. 性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。                                                                       本文地址

编译器一般按照几个字节对齐呢?本文中两个编译器默认按照类中最大类型长度来对齐,我么也可以使用语句#pragma pack(i)(i = 1,2,4,8,16)来设置对齐字节数目,vs还可以在项目属性-配置属性-c/c++-代码生成-结构成员对齐设置。

 

对齐规则如下:

  1. 如果设置了内存对齐为 i 字节,类中最大成员对齐字节数为j,那么整体对齐字节n = min(i, j)  (某个成员的对齐字节数定义:如果该成员是c++自带类型如int、char、double等,那么其对齐字节数=该类型在内存中所占的字节数;如果该成员是自定义类型如某个class或者struct,那个它的对齐字节数 = 该类型内最大的成员对齐字节数《详见实例4》)
  2. 每个成员对齐规则:类中第一个数据成员放在offset为0的位置;对于其他的数据成员(假设该数据成员对齐字节数为k),他们放置的起始位置offset应该是 min(k, n) 的整数倍
  3. 整体对齐规则:最后整个类的大小应该是n的整数倍
  4. 当设置的对齐字节数大于类中最大成员对齐字节数时,这个设置实际上不产生任何效果(实例2);当设置对齐字节数为1时,类的大小就是简单的把所有成员大小相加

我们通过以下几个实例来分析

实例1:(没有指定对齐字节,则n = 最大成员(int i)的大小4)

class node

{

char c;   //放在位置0,位置区间[0]

int i;      //4 = n, 那么放置起始位置应该是4的倍数,即4,位置区间为[4~7]

short s; //2 < n,那么放置起始位置应该是2的倍数,即8,位置区间为[8~9]

}

此时成员共占用[0~9]10个字节,还要整体对齐,大小应该是4的倍数,即12

 

实例2:(假设指定对齐字节为8,那么n = min(8,4) = 4)

class node

{

int i; //放在位置0,位置区间[0~3]

char c; //1 < n, 那么放置起始位置应该是1的倍数,即4,位置区间为[4]

short s; //2 < n,那么放置起始位置应该是2的倍数,即6,位置区间为[6~7]

}

成员共占据[0~7]8个字节,刚好是4的倍数,因此大小是8

 

实例3:(假设指定对齐字节是2,则n = min(2,4) = 2)

class node

{

char c; //放在位置0,位置区间[0]

int i; //4 > n, 那么放置起始位置应该是2的倍数,即2,位置区间为[2~5]

short s; //2 = n,那么放置起始位置应该是2的倍数,即6,位置区间为[6~7]

}

此时成员共占用[0~7]8个字节,刚好是4的倍数,因此大小是8

 

实例4:(按照默认设置)

class temp 

    char c; 
    int i; 
    short s1; 
};

由实例1可知,默认对齐情况下,temp的大小是12,temp的对齐字节数是:三个成员取最大的,即为4;

对于node,n = 其三个成员对齐字节数取最大,即等于t的对齐字节数,也就是 4。

class node

{

char c; //放在位置0,位置区间[0]

temp t; //4(temp的对齐字节数) = n, 那么放置起始位置应该是4的倍数,即4,位置区间为[4~15]

short s; //2 < n,那么放置起始位置应该是2的倍数,即16,位置区间为[16~17]

}

此时成员共占用[0~17]18个字节,还要整体对齐,大小应该是4的倍数,因此大小是20

 

实例5:(默然设置)

对于node,n = 其三个成员对齐字节数取最大,即等于d的对齐字节数,也就是 8。

class node

{

temp t; //放在位置0,位置区间[0~11]

double d; //8(temp的对齐字节数) = n, 那么放置起始位置应该是8的倍数,即16,位置区间为[16~23]

short s; //2 < n,那么放置起始位置应该是2的倍数,即24,位置区间为[24~25]

}

此时成员共占用[0~25]26个字节,还要整体对齐,大小应该是8的倍数,因此大小是32.

 


类继承时的内存对齐

考虑如下类

class A

{

int i;

char c1;

}

class B:public A

{

char c2;

}

class C:public B

{

char c3;

}

sizeof(C)结果是多少呢,gcc和vs给出了不同的结果,分别是8、16

gcc中:C相当于把所有成员i、c1、c2、c3当作是在一个class内部,(先继承后对齐)

vs中:对于A,对齐后其大小是8;对于B,c2加上对齐后的A的大小是9,对齐后就是12;对于C,c3加上对齐后的B大小是13,再对齐就是16 (先对齐后继承)

关于c++对象继承后的内存布局,更详细的分析可以《深度探索参考c++对象模型》第三章

 






本文转自tenos博客园博客,原文链接:http://www.cnblogs.com/TenosDoIt/p/3590491.html,如需转载请自行联系原作者

目录
相关文章
|
3月前
|
安全 C语言 C++
比较C++的内存分配与管理方式new/delete与C语言中的malloc/realloc/calloc/free。
在实用性方面,C++的内存管理方式提供了面向对象的特性,它是处理构造和析构、需要类型安全和异常处理的首选方案。而C语言的内存管理函数适用于简单的内存分配,例如分配原始内存块或复杂性较低的数据结构,没有构造和析构的要求。当从C迁移到C++,或在C++中使用C代码时,了解两种内存管理方式的差异非常重要。
128 26
|
8月前
|
存储 程序员 编译器
玩转C++内存管理:从新手到高手的必备指南
C++中的内存管理是编写高效、可靠程序的关键所在。C++不仅继承了C语言的内存管理方式,还增加了面向对象的内存分配机制,使得内存管理既有灵活性,也更加复杂。学习内存管理不仅有助于提升程序效率,还有助于理解计算机的工作原理和资源分配策略。
|
4月前
|
C语言 C++
c与c++的内存管理
再比如还有这样的分组: 这种分组是最正确的给出内存四个分区名字:栈区、堆区、全局区(俗话也叫静态变量区)、代码区(也叫代码段)(代码段又分很多种,比如常量区)当然也会看到别的定义如:两者都正确,记那个都选,我选择的是第一个。再比如还有这样的分组: 这种分组是最正确的答案分别是 C C C A A A A A D A B。
61 1
|
12月前
|
编译器 C++ 开发者
【C++】深入解析C/C++内存管理:new与delete的使用及原理(三)
【C++】深入解析C/C++内存管理:new与delete的使用及原理
206 3
|
10月前
|
存储 缓存 编译器
【硬核】C++11并发:内存模型和原子类型
本文从C++11并发编程中的关键概念——内存模型与原子类型入手,结合详尽的代码示例,抽丝剥茧地介绍了如何实现无锁化并发的性能优化。
407 68
|
7月前
|
存储 Linux C语言
C++/C的内存管理
本文主要讲解C++/C中的程序区域划分与内存管理方式。首先介绍程序区域,包括栈(存储局部变量等,向下增长)、堆(动态内存分配,向上分配)、数据段(存储静态和全局变量)及代码段(存放可执行代码)。接着探讨C++内存管理,new/delete操作符相比C语言的malloc/free更强大,支持对象构造与析构。还深入解析了new/delete的实现原理、定位new表达式以及二者与malloc/free的区别。最后附上一句鸡汤激励大家行动缓解焦虑。
|
8月前
|
安全 C语言 C++
彻底摘明白 C++ 的动态内存分配原理
大家好,我是V哥。C++的动态内存分配允许程序在运行时请求和释放内存,主要通过`new`/`delete`(用于对象)及`malloc`/`calloc`/`realloc`/`free`(继承自C语言)实现。`new`分配并初始化对象内存,`delete`释放并调用析构函数;而`malloc`等函数仅处理裸内存,不涉及构造与析构。掌握这些可有效管理内存,避免泄漏和悬空指针问题。智能指针如`std::unique_ptr`和`std::shared_ptr`能自动管理内存,确保异常安全。关注威哥爱编程,了解更多全栈开发技巧。 先赞再看后评论,腰缠万贯财进门。
346 0
|
9月前
|
存储 程序员 编译器
什么是内存泄漏?C++中如何检测和解决?
大家好,我是V哥。内存泄露是编程中的常见问题,可能导致程序崩溃。特别是在金三银四跳槽季,面试官常问此问题。本文将探讨内存泄露的定义、危害、检测方法及解决策略,帮助你掌握这一关键知识点。通过学习如何正确管理内存、使用智能指针和RAII原则,避免内存泄露,提升代码健壮性。同时,了解常见的内存泄露场景,如忘记释放内存、异常处理不当等,确保在面试中不被秒杀。最后,预祝大家新的一年工作顺利,涨薪多多!关注威哥爱编程,一起成为更好的程序员。
338 0
|
11月前
|
存储 缓存 C语言
【c++】动态内存管理
本文介绍了C++中动态内存管理的新方式——`new`和`delete`操作符,详细探讨了它们的使用方法及与C语言中`malloc`/`free`的区别。文章首先回顾了C语言中的动态内存管理,接着通过代码实例展示了`new`和`delete`的基本用法,包括对内置类型和自定义类型的动态内存分配与释放。此外,文章还深入解析了`operator new`和`operator delete`的底层实现,以及定位new表达式的应用,最后总结了`malloc`/`free`与`new`/`delete`的主要差异。
182 3
|
11月前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
552 4