内存四区的建立流程
流程说明
- 操作系统把物理硬盘代码load到内存
- 操作系统把c代码分成四个区
- 操作系统找到main函数入口执行
各区元素分析
栈区(stack)
—由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
堆区(heap)
一般由程序员分配和释放,若程序员不释放,程序运行结束时由操作系统回收。
malloc()、calloc()、free() 等函数操作的就是这块内存,这也是本章要讲解的重点。
注意:这里所说的堆区与数据结构中的堆不是一个概念,堆区的分配方式倒是类似于链表。
静态数据区(data area):
也称全局数据区,包含的数据类型比较多,如全局变量、静态变量、一般常量、字符串常量。
其中:
- 全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域
- 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。
- 常量数据(一般常量、字符串常量)存放在另一个区域。
注意:静态数据区的内存在程序结束后由操作系统释放
程序代码区
存放函数体的二进制代码
命令行参数区
存放命令行参数和环境变量的值,如通过main()函数传递的值
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int a = 0; // 全局初始化区(④区)
char *p1; // 全局未初始化区(③区)
int main()
{
int b; // 栈区
char s[] = "abc"; // 栈区
char *p2; // 栈区
char *p3 = "123456"; // 123456\0 在常量区(②),p3在栈上,体会与 char s[]="abc"; 的不同
static int c = 0; // 全局初始化区
p1 = (char *)malloc(10), // 堆区
p2 = (char *)malloc(20); // 堆区
// 123456\0 放在常量区,但编译器可能会将它与p3所指向的"123456"优化成一个地方
strcpy(p1, "123456");
}
内存分配方式有三种
- 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。
- 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。
栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。 - 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。
动态内存的生存期由我们决定,使用非常灵活,但问题也最多。 - 一般所说的堆栈(stack)往往是指栈,先进后出,它是一块内存区。
用以存放程序的局部变量,临时变量,函数的参数,返回地址等。
在这块区域中的变量的分配和释放由系统自动进行。不需要用户的参与。
而在堆(heap,先进先出)上的空间则是由用户进行分配,并由用户负责释放。
#include<stdio.h>
#include<stdlib.h>
//程序代码指令,define定义的常量---代码区(只读)
//全局(关键)变量/常量,静态(关键)变量/常量,常量字符串---静态区
//静态区的内容在总个程序的生命周期内都存在,由编译器在编译的时候分配。
//局部变量---栈区
//栈区上的内容只在函数的范围内存在,当函数运行结束,这些内容也会自动被销毁。其特点是效率高,但空间大小有限。
//由malloc或者realoc,calloc分配的内存区域---堆区
//堆区其生命周期由free决定。
#define PI 3.14 //
//全局变量
int a = 10;
int b[5] = { 1, 2, 3, 4, 5 };
int const c = 2;
void run2();
void main(){
int num = 10;
//对于int num = 10;的理解
//int 数据类型 编译器预算对象(变量)num分配4个字节内存空间大小
//num是内存中一段内存空间的标识(相当于门牌号),
//10 才是存储在这段内存空间里的数据
//详细的来说,“int num=10;”这段字符存储在代码区,数据10存储在栈区
//验证局部常量是否存储在栈区
run2();
//经过内存观察,开始局部常量cde的内存空间的数据是2
//当run2()函数执行完毕,局部常量cde的内存空间的数据发生变化
//因为静态区的内容在总个程序的生命周期内都存在,所以判断局部常量存储在栈区
char *p = "asdfadsf";//"asdfadsf"是字符串常量,在静态区,只读
//指针p是临时变量,在栈区
//验证 "asdfadsf" 只读
//*p = 'a';//编译不报错
printf("%s\n", p);//运行报错--“写入位置 0x001B692C 时发生访问冲突。”,由此证明"asdfadsf"是字符串常量,只读
//堆只能是malloc或者realoc,calloc,分配的内存才是堆
//例如以下代码
void *p1 = malloc(sizeof(int)* 1024);//malloc(sizeof(int)* 1024);分配的内存就是堆区,但是指针p1是在栈区
//堆的使用情况:①无法确定需要内存的大小;②需要的内存很大
//堆的注意点:堆和栈不同,堆的内存大小是由程序员分配的,必须由程序员手动释放(free(p1););
//对于栈来说,栈的内存大小由系统分配(栈的极限大约是1M,非常小),所以栈的内存空间是由系统回收的
//堆的内存大小是由程序员分配的,理论上可以占据系统中所有的内存(甚至能抢占栈的内存空间)
//因为栈的内存空间是由系统回收的,所有在回收的时候会消耗大量的cpu
char d = 'a';//变量d的数据在栈区中
system("pause");
}
//run1是程序代码,所以存储在代码区
void run1(){
while (1){
int x = 0;//x变量的内存空间在栈区,因为while循环,x每次循环都会初始化,所以系统不断的在栈上创建x的内存空间,回收x的内存空间,会消耗大量的cpu
printf("%d", x);
}
}
void run2(){
const cde = 2;//变量cde的数据在栈区,与cde是否是常量没关系
printf("%x\n",&cde);//获取cde的地址,检测run2()函数在main()执行完成之后,系统是否会回收cde指向的内存空间
}
int add(int a, int b){//形参a,b是临时变量,形参a,b的数据存储在栈区
return a + b;//a+b的操作会在寄存器中执行
}