「技术干货」一文搞懂C语言内存模型与栈

简介: 「技术干货」一文搞懂C语言内存模型与栈

一,内存模型

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

  • 1. BSS段(Block Started by Symbol): 用来存放程序中未初始化的全局变量的内存区域。
  • 2. 数据段(data segment): 用来存放程序中已初始化的全局变量的内存区域。
  • 3. 代码段(text segment): 用来存放程序执行代码的内存区域。
  • 4. 堆(heap):用来存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc分配内存时,新分配的内存就被动态添加到堆上,当进程调用free释放内存时,会从堆中剔除。
  • 5. 栈(stack):存放程序中的局部变量(但不包括static声明的变量,static变量放在数据段中)。同时,在函数被调用时,栈用来传递参数和返回值。由于栈先进先出特点。所以栈特别方便用来保存/恢复调用现场。

640.jpg

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

640.jpg



由上图可知:

  1. 0x0000 0000:保留区域, 最底层
  2. 区:用来存放程序代码和常量,只读(运行期会一直存在)
  3. 量区:一般常量,字符常量,只读(运行期会一直存在)
  4. 全局数据区:全局变量和静态变量,可读写(运行期会一直存在)
  5. 堆段: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变野指针误用
}

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)
{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 段中
}
相关文章
|
17天前
|
程序员 C语言
C语言库函数 — 内存函数(含模拟实现内存函数)
C语言库函数 — 内存函数(含模拟实现内存函数)
28 0
|
28天前
|
编译器 C语言 C++
【C语言】memset()函数(内存块初始化函数)
【C语言】memset()函数(内存块初始化函数)
26 0
|
28天前
|
编译器 C语言 C++
【C语言】memcpy()函数(内存块拷贝函数)
【C语言】memcpy()函数(内存块拷贝函数)
42 0
|
1月前
|
存储 C语言
c语言中动态内存分配
c语言中动态内存分配
10 0
|
5天前
|
存储 自然语言处理 编译器
振南技术干货集:振南当年入门C语言和单片机的那些事儿(3)
振南技术干货集:振南当年入门C语言和单片机的那些事儿(3)
|
19天前
|
存储 编译器 C语言
深入探索C语言动态内存分配:释放你的程序潜力
深入探索C语言动态内存分配:释放你的程序潜力
28 0
|
29天前
|
缓存 Java C#
【JVM故障问题排查心得】「Java技术体系方向」Java虚拟机内存优化之虚拟机参数调优原理介绍(一)
【JVM故障问题排查心得】「Java技术体系方向」Java虚拟机内存优化之虚拟机参数调优原理介绍
80 0
|
2天前
|
程序员 编译器 C语言
C语言知识:动态内存管理(malloc free calloc realloc)
C语言知识:动态内存管理(malloc free calloc realloc)
|
2天前
|
C语言
C语言:内存函数(memcpy memmove memset memcmp使用)
C语言:内存函数(memcpy memmove memset memcmp使用)
|
2天前
|
C语言
数据结构中顺序栈的进栈和出栈用C语言表示
数据结构中顺序栈的进栈和出栈用C语言表示
11 1