1. 内存区域
- C编译的程序占用的内存分为以下几个区域
- 代码区
- 全局区/静态区
- 栈区
- 堆区
- 划分
- 程序运行前: 代码区、全局区/静态区
- 程序运行后:栈区、堆区
2. 内存四区
-
代码区
作用:存放CPU执行的二进制机器指令
特点:只读、共享
-
栈区
栈是一种先进后出的内存结构,由编译器自动分配释放数据
主要存放函数的形式参数值、局部变量等
函数运行结束,相应栈变量会被自动释放
栈空间较小,不适合将大量数据存放在栈中 管理方式:编译器自动管理该区内存
空间大小:提前规定、较小
生命周期:函数使用完毕立即释放注意事项:不要返回局部变量的地址
//栈区上开辟的数据由系统进行管理,不需要程序员管理开辟和释放 int * func() { int a = 10; return &a; } //不管结果是否正确,这个值已经被释放了,不可以操作一块非法的内存空间 void test01() { int * p = func(); printf("a = %d\n", *p); printf("a = %d\n", *p); }
-
堆区
堆区由开发人员手动申请和释放,在释放之前,该块堆空间可一直使用
由程序员分配和释放,若程序员不释放,程序结束时由系统回收内存
堆空间一般没有软限制,只受限于硬件。会比栈空间更大,适宜存放较大数据
管理方式:开发人员手动申请和释放。
空间大小:较大
生命周期:手动释放之前一直存在,或程序结束由系统回收。
注意事项:主调函数中没有给指针分配内存,被调函数需要利用高级指针进行分屏代码示例
void allocateSpace(char * pp) { char * temp = malloc(100); memset(temp, 0, 100); strcpy(temp, "hello world"); pp = temp; } void test02() { char * p = NULL; allocateSpace(p); printf("%s\n", p); }
解决方式1
//利用返回值 char* allocateSpace3() { char *temp = malloc(100); memset(temp, 0, 100); strcpy(temp, "hello world"); return temp; } void test04() { char *p = NULL; p = allocateSpace3(); printf("%s\n", p); }
解决方式2
//利用高级指针 void allocateSpac2(char ** pp) { char * temp = malloc(100); memset(temp, 0, 100); strcpy(temp, "hello world"); *pp = temp; printf("aaa%s\n", *pp); } void test03() { char * p = NULL; allocateSpac2(&p); printf("%s\n", p); }
内存申请我们可使用三个函数来完成,分别为:malloc、calloc、realloc,内存释放我们只需要使用 free 函数。
-
malloc 函数:
原型:void *malloc(unsigned int num_bytes) 用法:分配长度为 num_bytes 字节的内存块。 说明:如果分配成功则返回指向被分配内存的指针,否则返回 NULL。
-
calloc 函数:
原型:void *calloc(int num_elems, int elem_size) 用法:为具有 num_elems 个长度为 elem_size 元素的数组分配内存。 说明:如果分配成功则返回指向被分配内存的指针,否则返回 NULL。
-
realloc 函数:
原型:void *realloc(void *mem_address, unsigned int newsize) 作用:改变 mem_address 所指内存区域的大小为 newsize 长度。 说明:如果重新分配成功则返回指向被分配内存的指针,否则返回 NULL。
-
free 函数:
原型:void free(void *p); 作用:释放指针 p 所指向的的内存空间。 说明:p所指向的内存空间必须是用 calloc,malloc,realloc 所分配的内存。如果 p 为 NULL则不做任何操作。
calloc案例:
void test01() { int *p = calloc(10,sizeof(int)); for (int i = 0; i < 10; ++i) { p[i] = i + 1; } for (int i = 0; i < 10; ++i) { printf("%d\n", p[i]); } if (p != NULL) { free(p); p = NULL; } }
realloc案例:
void test02() { int *p = malloc(sizeof(int)* 10); for (int i = 0; i < 10; ++i) { p[i] = i + 1; } for (int i = 0; i < 10; ++i) { printf("%d ", p[i]); } printf("%d\n", p); p = realloc(p, sizeof(int)* 200); printf("%d\n",p); for (int i = 0; i < 15; ++i) { printf("%d ", p[i]); } }
-
-
全局/静态区
全局/静态区存储全局变量、静态变量、常量,该区变量在程序运行期间一直存在
程序结束由系统回收
已初始化的数据放在data段,未初始化的数据放到bss段
该区变量当未初始化时,会有有默认值初始化
管理方式:编译器自动管理该区内存
生命周期:程序结束释放全局变量
extern int g_a = 10; //c语言中 默认全局变量前 加了关键字 extern void test01() { extern int g_a; printf("g_a = %d\n", g_a); }
静态变量
void func() { static int s_a = 10; //静态变量只初始化一次 s_a++; printf("%d\n", s_a); } void test03() { func(); func(); func(); } void test04() { static int s_a; //如果未初始化,默认为0 printf("%d\n", s_a); }
常量
const修饰的变量:
//全局常量 const int a = 10; //全局常量存放到常量区,收到常量区的保护 void test01() { //a = 20; //直接修改失败 int * p = &a; *p = 30; //间接修改 语法通过,运行失败 printf("a = %d ", a); //局部常量 const int b = 10; //b分配到了栈上,可以通过间接方式对其进行修改 //b = 30; //直接修改失败 int * p2 = &b; *p2 = 30; printf("b = %d\n", b); //间接修改成功,C语言下const修饰的局部常量为伪常量 } 结论: 全局修改失败,局部修改成功
字符串常量:
//1、字符串常量 是可以共享的 void test01() { char * p1= "hello world"; char * p2 = "hello world"; char * p3 = "hello world"; printf("%d\n",&"hello world"); printf("%d\n", p1); printf("%d\n", p2); printf("%d\n", p3); } //2、vs下 不可以修改字符串常量中的内存 void test02() { char * p1 = "hello world"; printf("%d\n", p1); printf("%c\n", p1[0]); //p1[0] = 'W'; //不允许修改 常量区内容 }