C程序中的内存分布
一个典型的C程序存储分区包含以下几类:
- Text段
- 已初始化数据段
- 未初始化数据段
- 栈
- 堆
进程运行时的典型内存布局
1. Text段
Text段通常也称为代码段,由可执行指令构成,是程序在目标文件或内存中的一部分,Text段通常放在栈或堆的下面,以防止堆栈溢出篡改其数据。
通常情况下,Text段是可共享的,对于需要频繁调用的程序,其在内存中只需要一份拷贝即可,如文本编辑器、C编译器、Shell等,因此text段通常设为只读以防止程序的突发性的修改。
2. 已初始化数据段
已初始化数据段,通常简单称作数据段,数据段占据程序虚拟地址空间的一部分,内部包括全局变量、静态变量(程序负责初始化这些变量)。需注意的是,数据段不是只读的,在运行时变量值是可以变动的。
数据段还可以更细的分为初始化只读区以及初始化可读写区。
举例:全局字符串 char s[] = “hello world”,全局变量 int debug=1,静态变量 static int i = 10 存储在初始化可读写区;另一种情况下,const char* string = “hello world”,字符串“hello world”存储在初始化只读区,string指针则存在初始化可读写区。
3. 未初始化数据段
未初始化数据段,通常称作“bss”段,名字来源于古老的汇编操作符命名 “block started by symbol”,段内的数据在程序开始执行之前被内核初始化为0值,通常开始于已初始化数据段的末尾处。段内包含初始化为0的全局变量/静态变量以及源码中未显示进行初始化的变量。
举例:变量 static int i; 全局变量 int j; 包含在BBS段中。
4. 栈
栈与堆是相互毗邻的,并且生长方向相反;当栈指针触及到堆指针位置,意味着栈空间已经被耗尽(如今地址空间越来越大,及虚拟内存技术发展,栈与堆可能放置在内存的任何地方,但生长方向依然还是相向的)。
栈区域包含一个LIFO结构的程序栈,其通常放置在内存的高地址处,在x86架构中,栈朝地址0方向生长,在其它架构也可能朝着相反的方向生长。栈指针寄存器跟踪栈顶位置,每当有数值被压入栈中,栈顶指针会被调整,在一个函数的调用过程中,压入的一系列数值被称作“栈帧”,栈帧至少包含一个返回地址。
栈存储着自动变量以及每次函数调用时保存的信息,每当函数被调用时,返回地址以及调用者的上下文环境例如一些机器寄存器都存储在栈中,新的被调用函数此时会在栈上重新分配自动/临时变量,这就是递归函数的工作原理。每当函数递归调用自己时,都会使用新的栈帧,因此当前函数实体内的栈帧内的变量不会影响另外一个函数实体内的变量。
5. 堆
堆通常用作动态内存分配,堆空间起始于BSS段的末尾,并向高地址处生长,堆空间通常由malloc, realloc 及 free管理,这些接口可能再使用brk/sbrk系统调用来调整大小,在一个进程中,堆空间被进程内所有的共享库及动态加载模块所共享。
示例-查看可执行文件的存储分配
注:size命令以字节为单位统计可执行程序的text, data, 及bss段(更多详情参考man page of size(1))
1. 检查下述C程序
编译后查看text/data/bss分布情况
2. 增加一个全局变量
编译后观察变化,bss区域增加了4字节
3. 再添加一个静态变量
编译后再看变化,bss区域增加了4字节
4. 初始化3中的静态变量看看
此时变量存储到了data段,data增加了4字节,bss减少了4字节
5.继续初始化2中的全局变量看看
此时静态变量也存储到了data段,data增加了4字节,bss减少了4字节
6. 添加一个全局const变量看看
此时“haha”字符串存储到了text段,并增加了5个字符,多出来的一个字符是\0,ptr指针存储到了data段,增加了4个字节。
注:在步骤4/5中,初始化的值若为0,编译器还是会将变量放入BSS区。感兴趣的同学可以动手编译看看。
文章翻译/修改自 https://www.geeksforgeeks.org/memory-layout-of-c-program/
C程序中的内存分布的更多相关文章
- 了解 JavaScript 应用程序中的内存泄漏
简介 当处理 JavaScript 这样的脚本语言时,很容易忘记每个对象.类.字符串.数字和方法都需要分配和保留内存.语言和运行时的垃圾回收器隐藏了内存分配和释放的具体细节. 许多功能无需考虑内存管理 ...
- [转载]Java应用程序中的内存泄漏及内存管理
近期发现测试的项目中有JAVA内存泄露的现象.虽然JAVA有垃圾回收的机制,但是如果不及时释放引用就会发生内存泄露现象.在实际工作中我们使用Jprofiler调用java自带的 jmap来做检测还是很 ...
- 高效使用 JavaScript 闭包,避免 Node.js 应用程序中的内存泄漏
在 Node.js 中,广泛采用不同形式的闭包来支持 Node 的异步和事件驱动编程模型.通过很好地理解闭包,您可以确保所开发应用程序的功能正确性.稳定性和可伸缩性. 闭包是一种将数据与处理数据的代码 ...
- [Swift通天遁地]七、数据与安全-(11)如何检测应用程序中的内存泄露
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(shanqingyongzhi)➤博客园地址:山青咏芝(https://www.cnblogs. ...
- (二)程序中的内存&&栈
一.程序运行为什么需要内存?基本概念? 内存是程序运行的立足之地,程序需要用内存来存储一些变量. 内存管理最终是由操作系统完成的,内存在本质上是一个硬件器件,由硬件系统提供:内存由操作系统统一管理,为 ...
- iOS程序中的内存分配 栈区堆区全局区
在计算机系统中,运行的应用程序的数据都是保存在内存中的,不同类型的数据,保存的内存区域不同.一.内存分区 栈区(stack) 由编译器自动分配并释放,存放函数的参数值,局部变量等.栈是系统数据结构,对 ...
- _CrtDumpMemoryLeaks报告程序中的内存泄露问题(简单示例代码)
// .h 文件 #pragma once class CConsoleDump { public: explicit CConsoleDump(LPCTSTR lpszWindowTitle = N ...
- 如何使用 VLD 检测程序中的内存泄漏?
下载地址:https://kinddragon.github.io/vld/ 下载 windows 安装包,进行安装即可,它会给你设置好 vs 的环境变量,使用时,直接在 vs ide 中包含即可. ...
- Linux下几款C++程序中的内存泄露检查工具
https://blog.csdn.net/gatieme/article/details/51959654
随机推荐
- PHP $$符号的作用与使用方法
php中$$符号的定义与作用 在PHP中单个美元符号变量($str),表示一个名为str的普通变量,它可以存储字符串.整数.数组.布尔等任何类型的值. 双美元符号的变量($$str):表示一个可变变量 ...
- Python - Django - 自定义一个中间件
中间件简介: 中间件是在 wsgi.py 之后,urls.py 之前,在全局操作 Django 请求和响应的模块 在 settings.py 中可以看到中间件的相关配置 该列表中的每一个元素都是一个类 ...
- python非官方扩展库
https://www.lfd.uci.edu/~gohlke/pythonlibs/ 安装方法: 1.下载自己需要的库文件 例如:Twisted-19.2.1-cp37-cp37m-win32.wh ...
- .net for TCP服务端 && 客户端
关键代码 详细代码请看示例代码 Service //创建套接字 IPEndPoint ipe = new IPEndPoint(IPAddress.Parse(ipaddress), port); / ...
- Mysql update多表联合更新
下面我建两个表,并执行一系列sql语句,仔细观察sql执行后表中数据的变化,很容易就能理解多表联合更新的用法 student表 ...
- SQL Server ---- 创建好的表修改不了字段
注意这个 是 sqlserver 2008 R2 当修改字段后出现来这个错误 解决方法 1.点击工具 选择选项 2.选择 designers 把阻止更改点掉就行了 建议,改好之后最 ...
- SSM学习系列
Spring+SpringMVC+MyBatis Spring+SpringMVC+MyBatis深入学习及搭建(一)——MyBatis的基础知识 Spring+SpringMVC+MyBatis深入 ...
- updatetable 违反并发性: UpdateCommand 影响了预期 1 条记录中的 0 条造成问题一种原因
数据库 表A ID UserName DeptID DeptName 1 张三 1 技术部 表B ID DeptName 1 市场部 当使 ...
- 三、SpringBoot整合Thymeleaf视图
目录 3.1 Thymeleaf视图介绍 3.2 创建SpringBoot项目 3.2 配置Thymeleaf 3.3 编写Demo 3.4 小结 3.1 Thymeleaf视图介绍 先看下官网的介绍 ...
- C指针的一些小细节
1 int *c;*c=4-->int *c;int b;c=&b;*c=4; 在使用指针之前,一定要将其初始化,当然,如果是赋予一个地址,就相当于使用的同时就进行了初始化.