目录
内存
栈内存
堆内存
数据段
代码段
注意
堆
例子
内存
Linux 操作系统为了更好更高效地使用内存,将 实际物理内存进行了映射,对应用程序屏蔽了物理内存的具体细节,有利于简化程序的编写 和系统统一的管理。
假设你正在使用的计算机实际物理内存只有 1GB 大小,而当前 系统运行了三个进程,Linux 会将 PM 中的某些内存映射为三个大小均为 4GB 的虚拟内存 ,让每个进程都以为自己独自拥有了完整的内存空间,这样极大地方 便了应用层程序的数据和代码的组织,
栈内存
栈内存以下简称栈
是从 0xC000 0000 往下增长的这部分内存区域,之所以 被称为“栈”是因为进程在使用这块内存的时候是严格按照“后进先出”的原则来操作的, 而这种后进先出的逻辑,就被称为栈
栈会随着进程的运行而不断 发生变化:一旦有新的函数被调用,就会立即在栈顶分配一帧内存,专门用于存放该函数内 定义的局部变量(包括所有的形参),当一个函数执行完毕返回之后,他所占用的那帧内存 将被立即释放
栈主要就是用来存储进程执行过程中所产生的局部变量的,当然为了可以实现函数的嵌 套调用和返回,栈还必须包含函数切换时当下的代码地址和相关寄存器的值,这个过程被称 为“保存现场”,等被调函数执行结束之后,再“恢复现场”。因此,如果进程嵌套调用了 很多函数,就会导致栈不断增长,但是栈的大小又是有一个最大限度的,这个限度一般是 8MB,超过了这个最大值将会产生所谓的“栈溢出”导致程序崩溃,所以我们在进程中不 宜嵌套调用太深的函数,也不要定义太多太大的局部变量
堆内存
堆内存(以下简称堆)是一块自由内存,原因是在这个区域定义和释放变量完全由你来 决定,即所谓的自由区。堆跟栈的最大区别在于堆是不设大小限制的,最大值取决于系统的 物理内存。 跟栈一样,会随着进程的运行而不断地增大或缩小
数据段
数据段实际上分为三部分,地址从高到底分别是.bss 段、.data 段和.rodata 段,三个 数据段各司其职:.bss 专门用来存放未初始化的静态数据,它们都将被初始化为 0,.data 段专门存放已经初始化的静态数据,这么初始值从程序文件中拷贝而来,而.rodata 段用来 存放只读数据,即常量,比如进程中所有的字符串、字符常量、整型浮点型常量等。
代码段
代码段实际上也至少分为两部分:.text 段和.init 段。.text 段用来存放用户程序代码, 也就是包括 main 函数在内的所有用户自定义函数,而.init 段则用来存储系统给每一个可 执行程序自动添加的“初始化”代码,这部分代码功能包括环境变量的准备、命令行参数的 组织和传递等,并且这部分数据被放置在了栈底。
注意
- 栈中的环境变量和命令行参数在程序一开始运行之时就被固定在了栈底(即紧挨着 内核的地方),且在进程在整个运行期间不再发生变化,假如进程运行时对环境变量的个数 或者值做了修改,则为了能够容纳修改后的内容,新的环境变量将会被拷贝放置到堆中。
- 栈和堆都是动态变化的,分别向下和向上增长,大小随着进程的运行不断变大变小
- 静态数据指的是:所有的全局变量,以及 static 型局部变量。
- 数据段的大小在进程一开始运行就是固定的,其中.rodata 存放程序中所有的常 量,.data 存放所有的静态数据,而如果静态数据未被初始化,则程序刚开始运行时系统将 会自动将他们统统初始化为 0 然后放置在.bss 段中,这么做的原因是要节省磁盘存储空间: 由于未初始化的静态数据在运行时一概会被初始化为 0,因此在程序文件中就没有必要保存 任何未初始化的变量的值了。
堆
堆内存被称为内存中的自由区,这是一个非常重要的区域,因为在此区域定义的内存的 生命周期我们是可以控制的,
堆内存的生命周期是:从 malloc( )/calloc( )/realloc( )开始,到 free( )结束,其分 配和释放完全由我们开发者自定义,这就给了我们最大的自由和灵活性,让程序在运行的过 程当中,以最大的效益使用内存
头文件#include <stdlib.h>
- void *malloc(size_t size)
- 在堆中申请一块大小为 size 的连续的内存
- size:对内存大小(字节)
- 返回新申请的内存基地址
- void *calloc(size_t n,size_t size)
- 在堆中申请一个具有 n 个元素的匿名数组,每个元素大小为 size
- 返回新申请的内存基地址
- void *realloc(void *ptr, size_t size);
- 将 ptr 所指向的堆内存大小扩展为 size
- 返回扩展后的内存的基地址
- 返回的基地址可能跟原地址 ptr 相同,也可能不同(即发生了迁移)
- 当 size 为 0 时,该函数相当于相当于 free(ptr);
- void free(void *ptr);
- 将指针 ptr 所指向的堆内存释放
- free(p)释放 p 所指向的堆内存,但是并不会改变 p 本身的值,也就是说释放了之后 p 就变成了一个野指针了,下次要引用指针 p 必须对他重新赋值
例子
#include <stdlib.h>
#include <stdio.h>
int *heap_array(int *old_ptr,int n);
void show_value(int *ptr);
int main(void)
{
int n, *p = (int *)malloc(1 * sizeof(int));
p[0] = 1;
while(1)
{
if(scanf("%d",&n)==0)break;
p= heap_array(p,n);
show_value(p);
}
free(p);
return 0;
}
int *heap_array(int *old_ptr, int n)
{
int size = old_ptr[0]+1;
printf("size = %d\n",size);
int *new_ptr;
new_ptr = (int*)realloc(old_ptr,(size * sizeof(int)));
new_ptr[0]=size;
printf("new_ptr[0]=%d\n",new_ptr[0]);
new_ptr[size-1]=n;
printf("new_ptr[%d]=%d\n",size-1,new_ptr[size-1]);
return new_ptr;
}
void show_value(int* ptr)
{
int i;
printf("--->>>");
for(i=1;i<ptr[0];i++)
{
printf("ptr[%d]=%d",i,ptr[i]);
}
printf("<<<-----\n");
}