怎么调试内存溢出的c++代码_【C/C++】内存对齐 到底怎么回事?

fe8c331809a8595b2131ad8a69254788.png

1 明确几个概念

代码分区:在使用C/C++编程时,我们定义的变量存在于内存中,而内存在C语言的角度上可以分为五大区。局部变量在栈区,静态/全局变量在全局区,动态申请的变量存在于堆区,const修饰的变量/字符常量存在于只读区。无论是什么样的变量,终究在内存中。

CPU取指,译码,执行:存在于内存中的目的是为了CPU通过总线的进行寻址,取指令,译码,执行取数据,内存与寄存器交互,然后CPU运算,再输出数据至内存。这个过程反复的,高速的执行。

CPU位数:在计算机中,最小的存储单元为字节(Byte),理论上任意地址(比如0x20000002,0x20000003,0x2000011...)都可以通过总线进行访问(CPU寻址),而每次寻址后,传输的数据大小跟CPU位数相关,常见的CPU位数有8位,16位,32位,64位。位数越高,单次操作执行的数据量越大,性能也就越强。

OS位数:操作系统一般与CPU位数相匹配,32位CPU可以寻址4GB内存空间,可以运行32位的OS。同样,64位的CPU可以运行32位的OS,也可以运行64位的OS。

Compiler:虽然编译器都是在翻译/编译代码,进行预处理(宏展开,头文件展开),编译(语法检查等),汇编(翻译为机器码),链接(重定位等)这四部分的工作。但是不同的编译器的内部默认设置以及用法会有所差异,常用的有GCC,VS,Clang,MinGW等。

2 指定平台

不谈平台,只谈编码,就是在耍流氓。---哈哈哈

这里的平台指的是三大件:CPU + OS + Compiler

本文中实验的平台是:Intel i7 + ubuntu16.04 + gcc5.4

有了上面的基本概念了解,就可以进行分析了。

3 为什么要内存对齐?

原因有两点:

CPU每次寻址都是要消费时间的,如果一次取不完数据就要取多次。比如int类型的变量a占4Byte,假设在内存中没有对齐(所谓对齐,指的是内存中数据的首地址是CPU单次获取数据大小的整数倍),且存放在0x00000003 - 0x00000006处(0x00000003不是4的整数倍)。那么每次取4字节(32位宽总线)的CPU第一次取到[0x00000000 - 0x00000003],只得到变量a的1/4数据,进而需要进行第二次取数[0x00000004 - 0x00000007],为了得到int类型的一个变量,却需要两次访问内存,并且还需要拼接处理,性能较低,这是其一

有些CPU(ARM架构的)在内存非对齐的情况下,执行二进制代码会崩溃,因为不是所有的硬件平台都能访问任意地址上的任意数据的。倘若代码移植到其他不支持的平台上,不具有可移植性,这是其二。

若在编译时,将分配的内存进行对齐,单次访问内存就可以获取数据,并且具有平台可移植性。

那谁来把我们编写的结构体,类中的成员变量进行对齐呢?

当然是编译器(Compiler)。那对齐的规则又是如何呢?

4 内存对齐规则

编译器提供手动指定对齐值的关键字 #pragma pack(N),可以手动设置对齐的字节数,比如#pragma pack(1),#pragma pack(4)等。这里即为N。

若没有手动指定,那么编译器就会默认将成员变量中最大的类型字节数设置为对齐值:m

1 整体对齐值:

首先计算对齐单位 n = min{N,m},然后整体对齐后的字节数应该为n的倍数,不够的在最后面填补占位。

2 成员对齐值:

首个成员的偏置地址(offset) = 0。

假定该成员的类型占字节数 j,那么本成员的偏移地址(offset):min{n, j}的整数倍。

5 代码实测

#include 

以上注释为理论分析

现在编译,并执行输出看看是否sizeof = 24。

be7b290b89dc08886ed20ac77f8ccf51.png

这里使用的GCC中的g++进行编译。

也可以用gcc,不过要链接c++的标准库(-lstdc++),否则会链接失败。

df119e679c5727e9e22de9d12a0bdbfd.png

这里实验结果与理论分析的一致。

另外:如果手动设置#pragma pack(4),后效果如何呢?

#pragma pack(4)

查看结果是否为sizeof = 20呢?

a24a10ce0f18152990bce70c6828c4b5.png

显然,是和分析的一致。

6 总结

这里以C++的类为例,进行内存对齐分析。关于C++的内存布局,以及含有virtual函数的类,实际上还会更复杂。简单的就如最基本的类,这和C中的struct是非常类似的。

掌握C++中类的内存对齐,有助于进一步理解C++对象模型。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值