C 语言中的内存四区
1 内存分区模型
C 程序运行时,操作系统将内存划分为4个区域:
1.栈区:由编译器自动分配和释放,存放函数的参数,局部变量等。
2.堆区:由程序员分配释放,若程序员未释放,则程序运行结束后,编译器自动释放。
3.全局区:存放全局变量,静态变量以及常量。
4.代码区:存放程序的二进制代码,由操作系统管理。
2 栈区
栈区:存放函数的参数值、局部变量等,由编译器自动分配和释放,通常在函数执行完毕后释放。
2.1 案例代码
#include <stdio.h>
#include <stdlib.h>
char *pfunc1()
{
//str是一个字符数组,开辟在栈区。
char str[100] = "hehe123";
return str;
}
int main()
{
char *p = NULL;
p = pfunc1();
//打印p指向的内容,%s是打印地址所指向内存空间的数据
printf("p = %s\n",p);
return 0;
}
2.2 程序运行结果
为什么会出现这样的运行结果那?
这个问题就是我们常说的由于返回局部变量的地址造成的。
2.3 程序运行分析
pfunc1函数在栈区,开辟一块内存,str数组在栈区开辟100个字节的大小。
字符串常量存储在全局区,因为str是一个字符数组,所以字符串的内容拷贝到数组里面,
pfunc1函数中将str的地址返回,main函数中的p1变量接收pfunc1函数的返回值。
pfunc1函数调用结束,开辟的栈区销毁,所以main函数中使用p1变量访问一个销毁的内存,结果出错。
3 堆区
堆区由程序员使用malloc申请和free释放,若是程序员未释放,程序结束后由操作系统回收。
3.1 案例代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *pfunc1()
{
char *tmp = (char *)malloc(100);
if(tmp == NULL)
{
return NULL;
}
strcpy(tmp,"hehe123");
return tmp;
}
int main()
{
int a = 0;
char *p =pfunc1();
if(p != NULL)
{
printf("p = %s,p = %p\n",p,p);
free(p);
p = NULL;
}
return 0;
}
3.2 程序运行结果
3.3 程序运行分析
pfunc1中使用malloc函数在堆区开辟一块内存,全局区上的字符串拷贝到堆区,tmp局部变量存在栈区上,
tmp指针指向的是堆区的地址,main函数中的p1变量接收pfunc1的返回值,p1指向堆区的地址,
所以pfunc1函数调用结束,tmp销毁后,使用p1依然能够正常访问。
4 全局区
全局区:存放全局变量、静态变量、常量。
全局变量
静态变量,由static 修饰的全局变量,static 修饰的局部变量。
常量,包括字符串常量,const 修饰的全局常量,const修饰的局部常量不在全局区。
全局区可以细分为.bss段(未初始化)和.data段(已初始化)。
全局区在程序结束后由系统释放。
4.1 案例代码
#include <stdio.h>
#include <stdlib.h>
char *pfunc1()
{
char *p1 = "hehe123";
return p1;
}
char *pfunc2()
{
char *p2 = "hehe123";
return p2;
}
int main()
{
char *p1 = NULL;
char *p2 = NULL;
p1 = pfunc1();
p2 = pfunc2();
//分别打印p1指向的内容和地址,%s是指打印指针所指向的内容
printf("p1 = %s,p1 = %p\n",p1,p1);
printf("p2 = %s,p2 = %p\n",p2,p2);
return 0;
}
4.2 程序运行结果
1.代码中char *p1 = “hehe123”;字符串是怎么存在指针里的?
字符串常量存放在全局区,指针变量存了字符串常量的地址。
2.分别在函数pfunc1和pfunc2里定义了一个相同的字符串,为什么在pfunc1和pfunc2里字符串地址是一样的。
字符串常量存在全局区,相同的字符串在全局区只开辟了一块空间存储字符串。不重复存这两个相同的字符串。
4.3 程序执行分析
main函数在栈区开辟一段内存,两个变量p1与p2开辟内存,pfunc1函数中变量p1、pfunc2函数中变量p2也在栈区开辟内存。
字符串存储在全局区,全局区的数据由操作系统管理,在程序运行结束后,自动回收。
pfunc1函数中的p1变量和pfunc2函数中的p2变量,存储的是字符串在全局变量的地址,然后通过函数返回给main函数中的变量p1、p2。在pfunc1、pfunc2函数调用结束后,这两个函数在栈区上的变量p1、p2也销毁。
main函数中的变量p1、p2变量存储着字符串的地址,由于字符串在全局区,所以通过p1、p2仍然可以正常访问。
5 代码区
- 代码区存放CPU执行的机器指令即程序汇编而成的二进制代码。
- 代码区是共享的,对于频繁执行的程序,内存中只需要保存一份即可。
- 代码区是只读的,只读的原因是防止程序意外修改了指令。