【转】C/C++ Memory Layout写的很好 .

C/C++ Memory Layout

2011-03-07 18:29 by 吴秦, 1529 visits, 网摘, 收藏, 编辑

为什么需要知道C/C++的内存布局和在哪可以可以找到想要的数据?知道内存布局对调试程序非常有帮助,可以知道程序执行时,到底做了什么,有助于写出干净的代码。本文的主要内容如下:

  • 源文件转换为可执行文件
  • 可执行程序组成及内存布局
  • 数据存储类别
  • 一个实例
  • 总结

源文件转换为可执行文件

源文件经过以下几步生成可执行文件:

  • 1、预处理(preprocessor):对#include、#define、#ifdef/#endif、#ifndef/#endif等进行处理
  • 2、编译(compiler):将源码编译为汇编代码
  • 3、汇编(assembler):将汇编代码汇编为目标代码
  • 4、链接(linker):将目标代码链接为可执行文件

编译器和汇编器创建的目标文件包含:二进制代码(指令)、源码中的数据;链接器将多个目标文件链接成一个;装载器吧目标文件加载到内存。

image

图1 源文件到可执行文件的步骤

 

可执行程序组成及内存布局

通过上面的小节,我们知道将源程序转换为可执行程序的步骤,典型的可执行文件分为两部分:

  • 代码段(Code),由机器指令组成,该部分是不可改的,编译之后就不再改变,放置在文本段(.text)。
  • 数据段(Data),它由以下几部分组:
    •  
      • 常量(constant),通常放置在只读read-only的文本段(.text
      • 静态数据(static data),初始化的放置在数据段(.data);未初始化的放置在(.bss,Block Started by Symbol,BSS段的变量只有名称和大小却没有值)
      • 动态数据(dynamic data),这些数据存储在堆(heap)或栈(stack

源程序编译后链接到一个以0地址为始地址的线性或多维虚拟地址空间。在Linux中,每个用户进程都可以访问4GB线性虚拟内存空间。而且每个进程都拥有这样一个空间,每个指令和数据都在这个虚拟地址空间拥有确定的地址,把这个地址称为虚拟地址(Virtual Address)。将进程中的目标代码、数据等的虚拟地址组成的虚拟空间称为虚拟存储器(Virtual Memory)。典型的虚拟存储器中有类似的布局:

  • Text Segment (.text)
  • Initialized Data Segment (.data)
  • Uninitialized Data Segment (.bss)
  • The Stack
  • The Heap

如下图所示:

image

图2 进程内存布局

当进程被创建时,内核为其提供一块物理内存,将虚拟内存映射到物理内存,这些都是由操作系统来做的。

数据存储类别

讨论C/C++中的内存布局,不得不提的是数据的存储类别!数据在内存中的位置取决于它的存储类别。一个对象是内存的一个位置,解析这个对象依赖于两个属性:存储类别、数据类型。

  • 存储类别决定对象在内存中的生命周期。
  • 数据类型决定对象值的意义,在内存中占多大空间。

C/C++中由(auto、 extern、 register、 static)存储类别和对象声明的上下文决定它的存储类别。

1、自动对象(automatic objects)

autoregister将声明的对象指定为自动存储类别。他们的作用域是局部的,诸如一个函数内,一个代码块{***}内等。操作了作用域,对象会被销毁。

  • 在一个代码块中声明一个对象,如果没有执行auto,那么默认是自动存储类别。
  • 声明为register的对象是自动存储类别,存储在计算机的快速寄存器中。不可以对register对象做取值操作“&”。

2、静态对象(static objects)

静态对象可以局部的,也可以是全局的。静态对象一直保持它的值,例如进入一个函数,函数中的静态对象仍保持上次调用时的值。包含静态对象的函数不是线程安全的、不可重入的,正是因为它具有“记忆”功能。

  • 局部对象声明为静态之后,将改变它在内存中保存的位置,由动态数据--->静态数据,即从堆或栈变为数据段或bbs段。
  • 全局对象声明为静态之后,而不会改变它在内存中保存的位置,仍然是在数据段或bbs段。但是static将改变它的作用域,即该对象仅在本源文件有效。此相反的关键字是extern,使用extern修饰或者什么都不带的全局对象的作用域是整个程序。

 

一个实例

下面我们分析一段代码:

程序中声明的变量a、b、c、d、e、pi的存储类别和生命期如下所述:

  • a是一个未初始化的全局变量,作用域为整个程序,生命期是整个程序运行期间,在内存的bbs段
  • b是一个未初始化的静态全局变量,作用域为本源文件,生命期是整个程序运行期间,在内存的bbs段
  • c是一个未初始化的局部变量,作用域为函数func体内,即仅在函数体内可见,生命期也是函数体内,在内存的栈中
  • d是一个未初始化的静态局部变量,作用域为函数func体内,即仅在函数体内可见,生命期是整个程序运行期间,在内存的bbs段
  • e是一个未初始化的局部变量,作用域为函数main体内,即仅在函数体内可见,生命期是main函数内,在内存的栈中
  • pi是一个局部指针,指向堆中的一块内存块,该块的大小为sizeof(int),pi本身存储在内存的栈中,生命期是main函数内
  • 新申请的内存块在堆中,生命期是malloc/free之间

用图表示如下:

image

图3 例子的内存布局

 

总结

本文介绍了C/C++中由源程序到可执行文件的步骤,和可执行程序的内存布局,数据存储类别,最后还通过一个例子来说明。可执行程序中的变量在内存中的布局可以总结为如下:

  • 变量(函数外):如果未初始化,则存放在BSS段;否则存放在data段
  • 变量(函数内):如果没有指定static修饰符,则存放在栈中;否则同上
  • 常量:存放在文本段.text
  • 函数参数:存放在栈或寄存器中

内存可以分为以下几段:

  • 文本段:包含实际要执行的代码(机器指令)和常量。它通常是共享的,多个实例之间共享文本段。文本段是不可修改的。
  • 初始化数据段:包含程序已经初始化的全局变量,.data。
  • 未初始化数据段:包含程序未初始化的全局变量,.bbs。该段中的变量在执行之前初始化为0或NULL。
  • 栈:由系统管理,由高地址向低地址扩展。
  • 堆:动态内存,由用户管理。通过malloc/alloc/realloc、new/new[]申请空间,通过free、delete/delete[]释放所申请的空间。由低地址想高地址扩展。

     

    作者:吴秦
    出处:http://www.cnblogs.com/skynet/

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 这是一个运行时错误,错误信息显示在stl_vector.h的第1034行,第9个字符处,出现了指向0xbebebebebebebebe地址的引用绑定错误。该地址需要4字节对齐,但未正确对齐导致错误。出现这个错误是因为vector的内存分配错误,导致指针指向了非法内存地址。这是未定义行为,在使用vector时应避免出现此类错误。 ### 回答2: 这是一个运行时错误,发生在C++程序执行时。该错误的含义是,程序试图将一个int变量与一个不对齐的内存地址绑定。在这种情况下,程序将尝试将一个int变量与0xbebebebebebebebe内存地址进行绑定,这个地址是一个未对其的地址。这个地址可能是无效的,或者指向程序内存的未知区域,这种情况下你的程序无法为其预测行为。 这个错误通常是由于向程序提供了错误的输入或参数而导致的。它还可能是由于程序在分配和释放内存时出现了问题,导致指针丢失或解除引用空指针,并尝试访问地址0xbebebebebebebebe。另一种可能性是使用了不同的编译器或库版本,从而导致内部数据结构不兼容。不同的编译器和库有不同的对齐方式和内部实现,因此使用不同的版本可能会导致数据结构的不兼容。 为了解决这个错误,你需要检查程序代码,特别是与内存分配和解除引用有关的代码。你可以尝试使用内存检测工具来查找指针丢失和空指针解除引用错误。也可以通过确保使用相同的编译器和库版本来避免数据结构不兼容问题。最后,使用良好的编码实践,如输入验证和处理意外情况,可以帮助你避免这种运行时错误。 ### 回答3: 本问题是关于C++程序运行时出现的一个错误,错误信息为“运行时错误:引用绑定到未对齐的地址0xbebebebebebebebe,对于类型'int',需要4字节对齐(stl_vector.h)0xbebebebebebebebe:注释:指针指向这里; <无法打印内存> 总结:undefinedbehaviorsanitizer:未定义行为。/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c /9/bits/stl_vector.h: 1043:9。” 这个错误是由于程序中对于类型int的变量没有正确对齐,导致引用绑定到了不正确的内存地址上,从而引起了运行时错误。 在C++中,变量的对齐是一个很重要的问题。一般情况下,编译器会根据变量的类型和所处的内存位置来进行自动对齐。如果变量没有按照正确的方式对齐,就会出现类似上面的运行时错误。 要解决这个问题,我们需要确定出程序中具体导致错误的代码位置。在这里,错误信息中指出了错误出现在stl_vector.h文件的第1043行,可以先尝试在这个文件中找到具体出问题的代码。然后,我们可以尝试为变量显式指定对齐方式,使用C++11的alignas语句来进行设置。这样可以确保变量按照指定的方式对齐,从而避免类似的运行时错误。 总之,针对这个问题,我们需要注意程序中的变量对齐问题,尽可能避免出现未对齐的情况,从而避免运行时错误。若出现此类错误,需要仔细分析具体情况,找到错误出现的位置,再采取合适的解决办法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值