当前位置 博文首页 > CW_qian的博客:8月22日笔记C语言基础(补5)内存管理1
C进程内存布局
????????任何一个程序,正常运行都需要内存资源,用来存放诸如变量、常量、函数代码等等。这些不同的内容,所存储的内存区域是不同的,且不同的区域有不同的特性。因此我们需要研究财经处内存布局,逐个了解不同内存区域的特性。
????????每个C语言进程都拥有一片结构相同的虚拟内存,所谓的虚拟内存,就是从实际物理内存映射出来的地址规范范围,最重要的特征是所有的虚拟内存布局都是相同的,极大地方便内核管理不同的进程。例如三个完全不相干的进程p1、p2、p3,它们很显然会占据不同区段的物理内存,但经过系统的变换和映射,它们的虚拟内存的布局是完全一样的。
VM2:进程P2看到的内存 |
VM1:进程P1看到的内存 |
VM3:进程P3看到的内存 |
将其中一个C语言含如进程的虚拟内存放大来看,会发现其内部包下区域:
1GB | 内核(kernel) | |
3GB | 栈(stack) | 0xc0000 0000 |
↑ ↓ | ||
堆(heap) | ||
数据段 | ||
代码段 | 0x0804 8000 | |
不可访问 |
????????虚拟内存中,内核区段对于应用程序而言是禁闭的,它们用于存放操作系统的关键性代码,另外由于 Linux 系统的历史性原因,在虚拟内存的最底端 0x0 ~ 0x08048000 之间也有一段禁闭的区段,该区段也是不可访问的。
虚拟内存中各个区段的详细内容:
内核(kernel) | |
栈(stack) | 环境变量 |
命令行参数 | |
局部变量 | |
↑ ↓ | |
堆(heap) | 用户自定义 内存空间 |
数据段 | .bss? 未初始化的静态数据 |
.data?已初始化的静态数据 | |
.rodata? 常量 | |
代码段 | .text?用户代码 |
.init?系统初始化代码 | |
不可访问 |
栈内存
void?func(int?a, int?*p) // 在函数 func 的栈内存中分配
{
????double?f1, f2; ???????// 在函数 func 的栈内存中分配
????... ??????????????????// 退出函数 func 时,系统的栈向上缩减,释放内存
}
int?main(void)
{
????int?m ?= 100; ?// 在函数 main 的栈内存中分配
????func(m, &m); ?// 调用func时,系统的栈内存向下增长
}
静态数据
C语言中,静态数据有两种:
int?a; // 全局变量,退出整个程序之前不会释放
void?f(void)
{
????static?int?b; // 静态局部变量,退出整个程序之前不会释放
????printf("%d\n", b);
????b++;
}
int?main(void)
{
????f();
????f(); // 重复调用函数 f(),会使静态局部变量 b 的值不断增大
}
数据段与代码段
int?a; ??????// 未初始化的全局变量,放置在.bss 中
int?b = 100; // 已初始化的全局变量,放置在.data 中
int?main(void)
{
????static?int?c; ??????// 未初始化的静态局部变量,放置在.bss 中
????static?int?d = 200; // 已初始化的静态局部变量,放置在.data 中
????
????// 以上代码中的常量100、200防止在.rodata 中
}
堆内存
????????堆内存(heap)又被称为动态内存、自由内存,简称堆。堆是唯一可被开发者自定义的区段,开发者可以根据需要申请内存的大小、决定使用的时间长短等。但又由于这是一块系统“飞地”,所有的细节均由开发者自己把握,系统不对此做任何干预,给予开发者绝对的“自由”,但也正因如此,对开发者的内存管理提出了很高的要求。对堆内存的合理使用,几乎是软件开发中的一个永恒的话题。
int?*p = malloc(sizeof(int)); // 申请1块大小为 sizeof(int) 的堆内存
bzero(p, sizeof(int)); ???????// 将刚申请的堆内存清零
*p = 100; // 将整型数据 100 放入堆内存中
free(p); ?// 释放堆内存
// 申请3块连续的大小为 sizeof(double) 的堆内存
double?*k = calloc(3, sizeof(double));
k[0] = 0.618;
k[1] = 2.718;
k[2] = 3.142;
free(k); ?// 释放堆内存