c语言内存管理和使用(一文搞懂C语言内存模型与栈)
在C语言中,内存可分用五个部分:
- 1. BSS段(Block Started by Symbol): 用来存放程序中未初始化的全局变量的内存区域。
- 2. 数据段(data segment): 用来存放程序中已初始化的全局变量的内存区域。
- 3. 代码段(text segment): 用来存放程序执行代码的内存区域。
- 4. 堆(heap):用来存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc分配内存时,新分配的内存就被动态添加到堆上,当进程调用free释放内存时,会从堆中剔除。
- 5. 栈(stack):存放程序中的局部变量(但不包括static声明的变量,static变量放在数据段中)。同时,在函数被调用时,栈用来传递参数和返回值。由于栈先进先出特点。所以栈特别方便用来保存/恢复调用现场。
嵌入式进阶教程分门别类整理好了,看的时候十分方便,由于内容较多,这里就截取一部分图吧。
需要的朋友私信【内核】即可领取
如下往上,分别是text段,data段,BSS段,堆,栈
由上图可知:
0x0000 0000:保留区域, 最底层
- 代码区:用来存放程序代码和常量,只读(运行期会一直存在)
- 常量区:一般常量,字符常量,只读(运行期会一直存在)
- 全局数据区:全局变量和静态变量,可读写(运行期会一直存在)
- 堆段:malloc/free的内存,malloc时分配,free时释放(向上增长)
未分配堆内存
0x4000 0000:动态链接库
未分配栈内存
栈段:局部变量,函数调用参数返回值(向上增长)
0xc000 0000 ~ 0xffff ffff:内核空间(1G)
二,栈详解栈(stack): 是由系统自动分配和释放,存放函数的参数值,返回值,局部变量等。其操作方式类似于数据结构中的栈。
2.1栈的申请1. 当在函数或块内部声明一个局部变量时,如:int nTmp; 系统会判断申请的空间是否足够,足够,在栈中开辟空间,提供内存;不够空间,报异常提示栈溢出。
2. 当调用一个函数时,系统会自动为参数当局部变量,压进栈中,当函数调用结束时,会自动提升堆栈。(可查看汇编中的函数调用机制)
2.2栈的大小栈是有一定大小的,通常情况下,栈只有2M,不同系统栈的大小可能不同。
在linux中,查看进程/线程栈大小,命令:
ulimit -s
$ ulimit -s
$ 8192
我的系统中栈大小为 8192, 有些系统为 10240, 具体查看自已系统栈大小
设置栈大小:
- 1. 临时改变栈大小:ulimit -s 10240
- 2. 开机设置栈大小:在/etc/rc.local中加入 ulimit -s 10240
- 3. 改变栈大小: 在/etc/security/limits.conf中加入* soft stack 10240
所以,在声明局部变量时,新手要特别注意栈的大小:
1. 对于局部变量,尽量不定义大的变量,如大数组(大于2*1024*1024字节)char buf[2*1024*1024]; // 可能会导致栈溢出
2. 对于内存较大或不知大小的变量,用堆分配,局部变量用指针,注意要释放char* pBuf = (char*)malloc(2*1024*1024); // char* 为局部变量 malloc的内存在堆free(pBuf);
3. 或定义在全局区中,static变量 或常量区中static char buf[2*1024*1024];
2.3栈的生长方向栈的生长方向和存放数据的方向相反,自顶向下
2.4 栈分配例子
int function( int var1 ,int var2)
{undefined
int var3;
int var4;
}
var1,var2,var3在栈中的图如下:
0xc000 0000 |
var1 |
0xc000 0000 - 4 |
var2 |
0xc000 0000 - 8 |
var3 |
0xc000 0000 - 12 |
var4 |
堆(heap):是用来存放动态申请或释放的区域。需要程序员分配和释放,系统不会自动管理,如果用完不释放,将会造成内存泄露,直到进程结速后,系统自动回收。
3.1 堆的目的为什么在堆呢?原因很简单,在栈中,大小是有限制的,能常大小为2M,如果需要更大的空间,那么就要用到堆了,堆的目的就是为了分配使用更大的空间。
3.2申请和释放
int function()
{undefined
char *pTmp = (char*) malloc(1024); // malloc在堆中分配1024字节空间
//pTmp 为局部变量,只占四字节
free(pTmp); // free为手动释放堆中空间
pTmp = NULL; // 防止pTmp变野指针误用
}
堆是可以申请大块内存的区域,但堆的大小到底有多大,下面分析下,以32位系统为例。
在linux中,堆区的内存申请,在32位系统中,理论上:2^32=4G,但如上面的内存分布图可知:内核占用1G空间。
0xFFFF FFFF |
1G内核空间 |
0xC000 0000 | |
0XBFFF FFF |
3G用户空间(包text段,data段,BSS段,堆,栈) |
0x0000 0000 |
如上所知,理论上,使用malloc最大能够申请空间大约3G。但这是理论值,因为实际中,还会包含代码区,全局变量区和栈区。
char *buf = (char*) malloc(3GB); // 理论上
如上面的图可知,堆是由低地址向高地址生长的
3.5 堆的注意事项堆虽然可以分配较大的空间,但有一些要注意的地方,否则会出现问题。
1. 释放问题:分配了堆内存,一定要记得手动释放,否则将会导致内存泄露
void* alloc(int size)
{undefined
char* ptr = (char*)malloc(size);
return ptr;
}
上面函数如果外部调用,没有释放,将内存不会释放造成泄露。
2. 碎片问题:如果频繁地调用内存分配和释放,将会使堆内存造成很多内存碎片,从而造成空间浪费和效率低下。
a) 对于比较固定,或可预测大小的,可以程序启动时,即分配好空间,如:某个对象不会超过500个,那个可先生成,object *ptr = (object*)malloc(object_size*500);
b) 结构对齐,尽量使结构不浪费内存
3. 超堆大小问题:如果申请内存超过堆大小,会出现虚拟内存不足等问题
a) 尽量不要申请很大的内存,如直需要,可采用内存数据库等
4. 分配是否成功问题:申请内存后,都在判断内存是否分配成功,分配成功后才能使用,否则会出现段错误
char * pTmp = (char*)malloc(102400);
if(pTmp == 0) // 一定在记得判断
{undefined
return false;
}
5. 释放后野指针问题:释放指针后,一定要记得把指针的值设置成NULL,防止指针被释放后误用
free(pTmp);
pTmp = NULL; // 防止变野指针
6. 多次释放问题:如果第5并没置NULL,多次释放将会出现问题。
四,例子
nt g_var = 0; // data段
int g_var1; // BSS 段
char g_str; // BSS 段
char g_str = “hello world”; // g_str data段 , hello world 字段常量区
char* g_ptr = NULL; // data段
int test()
{undefined
char l_var[1024]; // 栈
g_ptr = (char*)malloc(1024); // mall内存 椎中
static int g_int =1; // data 段中
}
免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com