C语言进程的内存布局:
程序:就是我们写好的代码并编译完成的那个二进制文件,它被存放与磁盘(硬盘)中,它是死的。
进程:把磁盘中的二进制文件"拷贝"到内存(内存条)中取执行它,让运行起来,它是活的。
所有的程序被执行起来之后,系统会为他分配各种资源内存,用来存放该进程中用到的各种变量、常量、代码等等。这些不容的内容将会被存放到内存中不同的位置 (区域),不同的内存区域他的特性是有差别。
每一个进程所拥有的内存都是一个虚拟的内存,所谓的虚拟内存是用物理内存中映射(投影)而来的,对于每一个进程而言所有的虚拟内存布局都是一样的。让每个进程都以为自己独自拥有了完整的内存空间。
物理内存(Physical Memory)
虚拟内存(Virtual Memory)
虚拟内存的布局(区域):
栈 (stack)
堆(heap)
数据段
代码段
栈空间
栈空间的特点:
- 空间非常有限,尤其在嵌入式的环境下,因此我们应该尽可能少去使用栈空间内存,特别是要存放比较大的数据。
ulimit -a
stack size (kbytes, -s) 8192 当前64位系统 默认只有8M栈内存
$ ulimit -s 10240 // 临时修改为 10M 重启后会回到默认值
- 每当一个函数被调用的时候, 栈空间会向下增长一段,用来存放该函数的局部变量
- 当一个函数退出的时候 , 栈空间会向上回缩一段,该空间的所有权将归还系统
- 栈空间的分配与释放,用户是无法干预的, 全部由系统来完成。
- 先进后出
静态变量
在C语言中有两种静态变量
全局变量: 定义在函数体之外的变量
静态的局部变量: 定义在函数体内部而且被 static 修饰的变量
int c ; // 在函数体之外,属于全局变量 --> 静态变量
int func(int argc , char * argv[] ) // argc argv 属于main函数的局部变量
{
int a ; // 局部变量
static int b ; // 静态局部变量
// 静态的局部变量 初始化语句只会被被执行一次
}
为什么会有静态变量?
- 当我们需要把一个变量引用到不同的函数内部甚至不在同一个.c 文件中,可以全局变量来实现。
- 当我们需要一个局部变量用来记录某个值, 并希望这个值不会被重新初始化的情况下可以使用静态的局部变量。(static 修饰)
数据段与代码段
数据段有哪些内容:
- .bss 未初始化的静态数据 , 会被自动初始化为0
- .data 已初始化的静态数据
- .rodata 存放常量数据 “Hello Myoung” , 不允许修改的(只读)
代码段中有那些内容:
- 用户的代码 (比如我们自己写函数func…main)
- 系统初始化代码,由编译器为我们添加的
堆内存
堆内存,又称为动态内存、自由内存、简称堆。唯一一个由开发者随意分配与释放的内存空间。具体的申请大小,使用的时常都是由我们自己来决定。
堆内存空间的基本特性:
- 相对与栈空间来说 ,堆空间大很多(堆的大小受限于物理内存),系统不会对对空间进行限制。
- 相对与栈空间来说, 堆内存是从下往上增长的。
- 堆空间的内存称为匿名内存, 不像栈空间那样有个名字,只能通过指针来访问
- 堆空间内存的申请与释放都是由用户自己完成,用户申请之后需要手动去释放,直到程序退出。
如何申请堆空间内存:
malloc (只是申请内存而已,并不会/清空)
malloc (向系统申请内存)
头文件: #include <stdlib.h>
函数原型: void *malloc(size_t size);
参数分析: size --> 需要申请的内存 (字节为单位)
返回值: 成功 返回一个指向成功申请到内存的指针(入口地址) 失败 返回 NULL
calloc (会把内存进行清空)
calloc (向系统申请内存)
头文件: #include <stdlib.h>
函数原型: void *calloc(size_t nmemb, size_t size);
参数分析: nmemb – > N 块内存(连续的) size – > 每一块内存的大小尺寸
返回值: 成功 返回一个指向成功申请到内存的指针(入口地址) 失败 返回 NULL
realloc
realloc (重新申请空间)
头文件: #include <stdlib.h>
函数原型: void *realloc(void *ptr, size_t size);
参数分析: ptr --> 需要 扩容/缩小 的内存的入口地址 size --> 目前需要的大小
返回值: 成功 返回修改后的地址 失败 NULL
清空内存的值
包含头文件<string.h>
bzero
函数原型:extern void bzero(void *s, int n)
参数分析:
功 能: 置字节字符串s的前n个字节为零且包括‘\0’
memset
函数原型:extern void *memset(void *buffer, int c, int count)
功 能:把buffer所指内存区域的前count个字节设置成c的值。
bzero和memset区别
1、bzero()不是ANSI C函数,其起源于早期的Berkeley网络编程代码,但是几乎所有支持套接字API的厂商都提供该函数;
2、memset()为ANSI C函数,更常规、用途更广。(建议使用)
如何释放堆空间内存:
free
free(释放堆内存)
头文件: #include <stdlib.h>
函数原型: void free(void *ptr);
参数分析: ptr --> 需要释放的内存的入口地址
返回值; 无
内存拷贝函数:
memcpy
strcpy
strncpy
sprontf
…
总结
- 使用malloc 申请内存时 , 内存中的值时随机值 , 可以使用memset清空
- calloc 申请内存时 , 内存中的值会被初始化为 0
- free 只能释放堆空间的内存,不能释放其它区域的内存
释放内存的含义:
1.释放内存仅仅意为着,当前内存的所有权交回给系统
2.释放内存并不会清空内存的内容
3.也不会改变指针的指向,需要手动把指针指向NULL ,不然就成野指针了