C语言内存模型详解

原创 2018年04月15日 06:31:19

C语言内存模型详解

second60  20180415

1 内存模型

C语言中,内存可分用五个部分:

1. BSS段(Block Started by Symbol): 用来存放程序中未初始化的全局变量的内存区域。

2. 数据段(data segment): 用来存放程序中已初始化的全局变量的内存区域。

3. 代码段(text segment): 用来存放程序执行代码的内存区域。

4. 堆(heap):用来存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc分配内存时,新分配的内存就被动态添加到堆上,当进程调用free释放内存时,会从堆中剔除。

5. 栈(stack):存放程序中的局部变量(但不包括static声明的变量,static变量放在数据段中)。同时,在函数被调用时,栈用来传递参数和返回值。由于栈先进先出特点。所以栈特别方便用来保存/恢复调用现场。

 

APUE中的一个典型C内存空间分布图

 

如下往上,分别是text段,data段,BSS段,堆,栈

Linux下32位环境的用户空间内存分布情况

 

 

由上图可知:

0x0000 0000:保留区域, 最底层

代码区:用来存放程序代码和常量,只读(运行期会一直存在)

常量区:一般常量,字符常量,只读(运行期会一直存在)

全局数据区:全局变量和静态变量,可读写(运行期会一直存在)

堆段:malloc/free的内存,malloc时分配,free时释放(向上增长)

未分配堆内存

0x4000 0000:动态链接库

未分配栈内存

栈段:局部变量,函数调用参数返回值(向上增长)

0xc000 0000 ~ 0xffff ffff:内核空间(1G)

 

2栈详解

(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)

{

int  var3;

int  var4;

}


var1,var2,var3在栈中的图如下:

0xc000 0000

var1

0xc000 0000 - 4

var2

0xc000 0000 - 8

var3

0xc000 0000 - 12

var4

 

3 堆详解

堆(heap:是用来存放动态申请或释放的区域。需要程序员分配和释放,系统不会自动管理,如果用完不释放,将会造成内存泄露,直到进程结速后,系统自动回收。

 

3.1 堆的目的

为什么在堆呢?原因很简单,在栈中,大小是有限制的,能常大小为2M,如果需要更大的空间,那么就要用到堆了,堆的目的就是为了分配使用更大的空间。

3.2申请和释放

int  function()

{

char *pTmp = (char*) malloc(1024);   // malloc在堆中分配1024字节空间

  //pTmp 为局部变量,只占四字节

free(pTmp); // free为手动释放堆中空间

pTmp = NULL; // 防止pTmp变野指针误用

}

 

3.3堆的大小

堆是可以申请大块内存的区域,但堆的大小到底有多大,下面分析下,以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.4 堆的生长方向

   如上面的图可知,堆是由低地址向高地址生长的

 

3.5 堆的注意事项

堆虽然可以分配较大的空间,但有一些要注意的地方,否则会出现问题。

 

1. 释放问题:分配了堆内存,一定要记得手动释放,否则将会导致内存泄露

void*  alloc(int size)

{

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)   // 一定在记得判断

{

return false;

}

5. 释放后野指针问题:释放指针后,一定要记得把指针的值设置成NULL,防止指针被释放后误用

free(pTmp);

pTmp = NULL; // 防止变野指针

6. 多次释放问题:如果第5并没置NULL,多次释放将会出现问题。

 

4 例子

 

int  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()

{

char l_var[1024]; //

g_ptr = (char*)malloc(1024); // mall内存 椎中

static  int  g_int =1; // data 段中

}

 

复习下C语言内存模型,网上虽然有很大内存模型的介绍,但是比较零散,本文详细地介绍了其中的细节,当然也有不完善的地方,欢迎补充。

 

 

 

 

 

 

C语言内存模型(内存组织方式)

我们知道,C程序开发并编译完成后,要载入内存(主存或内存条)才能运行(请查看:载入内存,让程序运行起来),变量名、函数名都会对应内存中的一块区域。 内存中运行着很多程序,我们的程序只占用一部分空...
  • qq_1017097573
  • qq_1017097573
  • 2015-06-04 17:21:57
  • 1100

C语言内存模型及运行时内存布局

BSS段(bss segment) 通常是指用来存放程序中未初始化的全局变量的一块内存区域。BSS是英文Block Started by Symbol的简称。BSS段属于静态内存分配。 ...
  • u013033371
  • u013033371
  • 2013-12-03 18:04:23
  • 4146

C语言的内存模型

本文以IA-32环境为例. 一、内存类型      1.代码段(Code or Text):代码段由程序中的机器码组成。在C语言中,程序语句进行编译后,形成机器代码。在执行程序的过程中,CPU的程序...
  • fghhslk
  • fghhslk
  • 2013-10-24 23:39:53
  • 938

C语言之内存四区模型和函数调用模型

1、内存四区模型 Ø流程说明 1、操作系统把物理硬盘代码load到内存 2、操作系统把c代码分成四个区 3、操作系统找到main函数入口执行 ...
  • wu5215080
  • wu5215080
  • 2014-08-28 18:48:55
  • 14047

C语言中的内存模型

写了好几年的程序了,大部分时候都是在用C++,剩下用了点java。用java的时候基本上没有考虑过内在模型的问题,因为使用频率并不高,也没有碰到过什么大的问题。用C++这么多年了,但是对内存模型其实还...
  • Code_My_Life
  • Code_My_Life
  • 2015-07-30 19:19:56
  • 521

C语言之指针专题三:理解指针要和内存四区模型和函数调用模型相结合

 1)、理解指针要把内存四区模型和函数调用模型相结合 2)、主调,被调函数谁分配内存 主调用函数分配内存,被调用函数使用内存---输入 被调用函数分配内存把结果输出给主调用...
  • wu5215080
  • wu5215080
  • 2014-08-30 22:21:52
  • 2531

C语言解释器Java版-0-内存模型

前提说明 由于工作原因,需要实现一个Java版的C语言解释器。在实现的过程中,遇到了很多问题、学到了很多东西,特将实现过程记录下来,备忘的同时,也供后来人学习参考。 在实现之前,已有的内...
  • imu2008
  • imu2008
  • 2015-11-01 17:11:27
  • 219

二级指针三种内存模型

二级指针做输出模型#define _CRT_SECURE_NO_WARNINGS #include #include #include //指针做输出:被调用函数分配内存 -----OK ...
  • saizo123
  • saizo123
  • 2017-08-13 13:02:18
  • 290

C——提高(2)字符串一级指针、二级指针内存模型

字符串一级指针C语言里没有实质的字符串类型,通过字符数组来模拟。 C语言的字符串是以0结尾的字符串。 C语言字符串的内存分配 可以分配在堆上、栈上、全局区上。1、字符串一级指针内存模型buf数组与...
  • wuseyukui
  • wuseyukui
  • 2015-12-16 22:39:55
  • 1629

java存储模型与C语言存储模型(二)

C程序开发并编译完成后,是要载入内存(主存或内存条)才能运行,变量名、函数名都会对应内存中的一块区域。...
  • Daxiongye123
  • Daxiongye123
  • 2017-08-05 22:30:11
  • 121
收藏助手
不良信息举报
您举报文章:C语言内存模型详解
举报原因:
原因补充:

(最多只允许输入30个字)