Loading... # 目录 1. 动态内存分配的相关概念 2. 静态分配和动态分配 3. 动态内存申请相关函数 4. 内存泄漏 --- # 动态内存分配的相关概念 1. 在数组中,介绍过数组长度是预先定义好的,在整个程序中<span style="color:#FF0000">固定不变</span>。 2. 但是在实际的编程中,往往会发生这种情况,即所需的<span style="color:#FF0000">**内存空间取决于实际输入的数据**</span>,而无法预先确定。 3. 为了解决上述问题,C语言提供了一些内存管理函数,这些内存管理函数可以按需要<span style="color:#FF0000">**动态的分配**</span>内存空间,也可以把不在使用的空间回收再次利用。 <div class="tip inlineBlock warning"> 我们的中心是使用C语言提供的内存管理函数。 </div> # 静态分配和动态分配 ## 静态分配 1. 在程序编译或运行过程中,按事先规定大小分配内存空间的分派方式. 2. 必须事先知道所需空间的大小. 3. 分配在栈区或全局区,一般以数组的形式. 4. 按计划分配. ## 动态分配 1. 在程序运行过程中,根据需要大小自由分配所需空间. 2. 按需分配. 3. 分配在堆区空间,一般使用特定的函数进行分配. 4. * 到底动态内存是从哪里分配的呢? <div class="tip inlineBlock share"> 内存分区分为:堆区、栈区、全局区、文字常量区、代码区。 </div> <div class="tip inlineBlock success"> 动态类型分配是围绕堆区 </div> --- # 动态内存申请相关函数 ##### 1、分配内存空间函数 malloc 函数原型:void *malloc(unsigned int num_bytes); 调用形式:(类型说明符*) malloc (需要申请空间大小的字节数) 功能说明: * 在内存的动态存储区(堆区)中分配一块长度为size字节的连续区域,用来存放类型说明符指定的类型。 * 函数原型返回void*指针,指向空间的起始地址,使用时<span style="color:#FF0000">必须做相应的强制类型转换</span>。 * 分派的内存空间不确定,一般使用memset初始化(进行清空)。 返回值: * 分配成功:返回空间的起始地址 * 分配失败:NULL 注意: 1. 在调用malloc之后,一定要判断一下,是否申请成功。 2. 如果多次malloc申请的内存,第1次和第二次申请的内存不一定是连续的。 ##### 2、free 函数(释放内存函数) 头文件:stdilb.h 函数定义:void free(void *ptr) 函数说明:free函数释放ptr指向的内存,空间使用权限的回收,不一定会清空数据。 <div class="tip inlineBlock warning"> 注意:ptr指向的内存比须是malloc calloc rellloc动态申请的内存 </div> ###### 案例1:从堆区申请 一个int空间 ```C #include<stdio.h> #include<stdlib.h> int main(int argc, char* argv[]) { int* addr = NULL; addr = (int*)malloc(sizeof(int)); if (addr == NULL) { //申请失败 printf("malloc error"); return 0; } //直接打印看看 printf("*addr = %d\n", *addr); //不确定 //得到一个随机的值-842150451 //那我们就要对堆区空间初始化(清零) memset(addr, 0, sizeof(int)); //现在我们再去打印 printf("*addr = %d\n", *addr); //得到0 //先在我们可以对addr空间 进行 读写 操作 *addr = 1000; printf("*addr = %d", *addr); //用完空间后记得释放 free(addr); return 0; } ``` ###### 案例2:从堆区申请一个数组 数组的大小(动态数组) 1. 从键盘获取 用户要申请的数组大小 2. 根据大小 从堆区申请空间 3. 对空间的读写操作 4. 释放该空间 ```C #include<stdio.h> #include<stdlib.h> int main(void) { int n = 0, i = 0; int* arr = NULL; //1、获取空间大小 printf("请输入元素的个数:"); scanf("%d", &n); //2、根据大小从堆区申请空间 arr = (int*)malloc(n * sizeof(int)); if (arr == NULL) { //perror 错误输出 perror("malloc"); return 0; } memset(arr, 0, n * sizeof(int)); //对arr的读写操作 printf("请输入%d个int数据\n", n); for (i = 0; i < n; i++) { scanf("%d", arr + i); } //遍历数组 for (i = 0; i < n; i++) { printf("%d ", *(arr + i)); } //释放空间 free(arr); } ``` ##### 3. calloc函数 头文件:stdlib.h 函数原型:void * calloc(size_t nmemb, siaet size); 功能:在内存的堆区中,申请 nmemb 块,每块的大小为size个字节的连续区域(总大小=nmemb*size) 参数:size_t 实际是无符号整型,它是在头文件中,用typdef 定义出来的 返回值: * 申请成功:返回申请的内存的首地址 * 申请失败:返回NULL 特点:申请的空间自动清零,不需要使用memset 例如 : ```C char *p=(char*)calloc(3,100); ``` ###### malloc于calloc的区别 malloc和calloc函数都是用来申请内存的 区别: 1. 函数名字不一样 2. 参数个数不一样 3. malloc申请的内存,内存中存放的内容是随机的,不确定的。而calloc函数申请的内存中内容为0 ```C #include<stdio.h> #include<stdlib.h> int main(void) { int n = 0, i = 0; int* arr = NULL; //1、获取空间大小 printf("请输入元素的个数:"); scanf("%d", &n); //2、根据大小从堆区申请空间 arr = (int*)calloc(n, sizeof(int));//自动清零,不需要使用memset if (arr == NULL) { //perror 错误输出 perror("malloc"); return 0; } //对arr的读写操作 printf("请输入%d个int数据\n", n); for (i = 0; i < n; i++) { scanf("%d", arr + i); } //遍历数组 for (i = 0; i < n; i++) { printf("%d ", *(arr + i)); } //释放空间 free(arr); } ``` ##### 4、realloc动态追加或减少空间 头文件:stdlib.h 函数原型:void* realloc(void *s, unsigned int new_size) 功能:在原先s指向的内存基础上重新申请内存,新的内存的大小为 new_size 个字节, 如果原先内存后面有足够打的空间,就追加,如果后边的内存不够用,则relloc函数会在堆区找一个new_size个字节大小的内存申请,将原先内存中的内容拷贝过来,然后释放原先的内存,最后**返回新内存的地址**。 参数: * s:原先开辟内存的首地址 * newsize:新申请的空间的大小 返回值: * 新申请的内存的首地址 案例: ```C char* p; p = (char*)malloc(100); //在100个字节后面追加50个字节 p = (char*)realloc(p, 150); ``` 案例: ```C #include<stdio.h> #include<stdlib.h> int main(void) { int n = 0, i = 0; int* arr = NULL; int new_n; //1、获取空间大小 printf("请输入元素的个数:"); scanf("%d", &n); //2、根据大小从堆区申请空间 arr = (int*)calloc(n, sizeof(int));//自动清零,不需要使用memset if (arr == NULL) { //perror 错误输出 perror("malloc"); return 0; } //对arr的读写操作 printf("请输入%d个int数据\n", n); for (i = 0; i < n; i++) { scanf("%d", arr + i); } //遍历数组 for (i = 0; i < n; i++) { printf("%d ", *(arr + i)); } printf("\n"); //再追加new_n个元素 printf("请输入新增的元素个数:"); scanf("%d", &new_n); arr = (int*)realloc(arr, (n + new_n) * sizeof(int)); printf("请输入新增的%d个int数据\n", new_n); for (i = 0; i < new_n; i++) { scanf("%d", arr + n + i); } //遍历 for (i = 0; i < (n + new_n); i++) { printf("%d ",arr[i]); } printf("\n"); //释放空间 free(arr); } ``` <div class="tip inlineBlock warning"> 注意:一定要保留realloc的返回值 </div> ![运行结果](https://blog.fivk.cn/usr/uploads/2021/02/4105497199.png) # 内存泄漏 内存泄漏概念: 申请的内存,首地址丢了,找不到了,再也没法使用了,也没法释放了。这块内存就被泄漏了。 # 使用堆区空间的注意事项 1. 指向堆区空间的指针变量不要随意更改指向 ```C int* p = (int*)calloc(1, sizeof(int)); int num = 10; p = & num //p指向了num 导致calloc申请的空间泄漏 ``` 2. 不要操作已经释放的空间 ```C p = (int*)calloc(1, sizeof(int)); *p = 1000; free(p); printf("%d", *p);//内容不确定 ``` 3. 不要对堆区空间重复释放 ```C p = (int*)calloc(1, sizeof(int)); free(p); free(p);//多次释放 段错误 ``` ##### 防止空间多次释放 ```C int* p = (int*)calloc(1, sizeof(int)); if (p != NUll) { free(p); p = NULL; } ``` 最后修改:2021 年 02 月 06 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 如果觉得我的文章对你有用,请随意赞赏