C语言动态内存分配图文讲解
作者:戊子仲秋
思维导图
1.为什么存在动态内存分配
我们现在学习了一些内存开辟的方式:
int main() { int i;//在内存栈区开辟4个字节空间 char arr[5];//在栈空间上开辟5个字节的连续空间 return 0; }
但是,这样开辟的内存是静态的,固定的:
1. 空间开辟大小是固定的。
2. 数组在申明的时候,必须指定数组的长度,它所需要的内存在编译时分配。
如果想要在编译过程中开辟空间,就需要用到动态内存。
2.动态内存函数的介绍
2.1 malloc
void* malloc (size_t size)
2.2 free
void free (void* ptr)
例:
#include <stdio.h> #include <stdlib.h> int main() { //申请40给字节,用来存放10个整形 int* p = (int*)malloc(40);//malloc申请的空间不会初始化 if (p == NULL) //直接返回起始地址 { perror("malloc");//如果空间开辟失败要报错并返回 return 1; } //使用空间 int i = 0; for (i = 0; i < 10; i++) { printf("%d\n", *(p + i)); } //释放申请的内存 free(p); p = NULL; return 0; }
输出:
输出:
-842150451
-842150451
-842150451
-842150451
-842150451
-842150451
-842150451
-842150451
-842150451
-842150451
malloc不会自己初始化,所以打印随机值。
例:
#include <stdio.h> #include <stdlib.h> int main() { //申请40给字节,用来存放10个整形 int* p = (int*)malloc(40);//malloc申请的空间没有初始化 if (p == NULL) //直接返回起始地址 { perror("malloc");//如果空间开辟失败要报错并返回 return 1; } //使用空间 int i = 0; for (i = 0; i < 10; i++) { *(p + i) = i + 1;//初始化赋值 printf("%d ", *(p + i)); } //释放申请的内存 free(p); p = NULL; return 0; }
输出:
输出:1 2 3 4 5 6 7 8 9 10
2.3 calloc
void* calloc (size_t num, size_t size)
例:
#include <stdio.h> #include <stdlib.h> int main() { int* p = (int*)calloc(10, sizeof(int));//10是要初始化的个数,sizeof(int)是每个的大小 if (NULL == p) { perror("calloc");//判断内存是否申请成功 return 1; } //使用 int i = 0; for (i = 0; i < 10; i++) { printf("%d ", *(p + i));//calloc申请空间后,会把空间初始化成0 } //再返回起始地址 //释放 free(p); p = NULL; }
输出:
输出:0 0 0 0 0 0 0 0 0 0
2.4 realloc
void* realloc (void* ptr, size_t size)
realloc函数可以追加更多动态内存。
例:
#include <stdio.h> #include <stdlib.h> int main() { int* p = (int*)malloc(5 * sizeof(int));//开辟一段动态内存20个字节 if (NULL == p) { perror("malloc");//检查是否创建成功 return 1; } //使用 int i = 0; for (i = 0; i < 5; i++) { *(p + i) = i + 1; } //不够用,增加5个整形的空间 int* ptr = (int*)realloc(p, 10 * sizeof(int));//这里是开辟到40个字节 //realloc函数开辟内存空间有两种情况: //1.原内存块后面空间足够,在原内存块后面追加内存 //2.原内存块后面空间不够,另外找一片区域开辟内存,将原内存块释放 if (ptr != NULL) { p = ptr;//为什么不直接用p接收? //如果内存追加失败,直接用p接收的话,原本已经开辟的动态内存空间就会被覆盖 } for (i = 0; i < 10; i++) { printf("%d ", *(p + i)); } //释放 free(p); p = NULL; return 0; }
输出:
输出:1 2 3 4 5 -842150451 -842150451 -842150451 -842150451 -842150451
3.常见的动态内存错误
例1:
开辟动态内存记得要判断,最后释放内存。
#include <stdio.h> #include <stdlib.h> int main() { int* p = (int*)malloc(100); //内存开辟后没有判断是否开辟成功 int i = 0; for (i = 0; i < 10; i++) { *(p + i) = 0;//危险代码,我们无法知道是否存在非法访问 } //并且最后也没有将开辟的内存还给操作系统 return 0; }
例2:
动态内存开辟了多少就用多少,小心越界访问。
#include <stdio.h> #include <stdlib.h> int main() { int* p = (int*)malloc(100);//开辟了100字节空间 //判断 if (p == NULL) { perror("malloc"); return 1; } int i = 0; for (i = 0; i < 100; i++)//造成越界访问 { *(p + i) = 0;//一个整形4个字节 } //释放 free(p); p = NULL; return 0; }
例3:
不要乱释放其他内存空间。
int main() { int a = 10; int* p = &a; //你没有权限乱释放其他的内存空间 free(p);//不能对栈区的内存释放 return 0; }
例4:
不要多次释放内存空间。
#include <stdio.h> #include <stdlib.h> int main() { //开辟空间 int* p = (int*)malloc(100); //判断 if (p == NULL) { perror("malloc"); return 1; } //使用 int i = 0; for (i = 0; i < 25; i++) { *p = i; p++;//p指针不断往后移动 } //释放的时候指针应该指向起始地址,否则程序又会出错 free(p); p = NULL; return 0; }
例5:
#include <stdio.h> #include <stdlib.h> int main() { //创建 int* p = (int*)malloc(100); //判断 if (p == NULL) { return 1; } //释放 free(p); free(p);//已经释放了,重复释放会导致程序出错 return 0; }
这就要说到最后置为空指针的好处了:
#include <stdio.h> #include <stdlib.h> int main() { //创建 int* p = (int*)malloc(100); //判断 if (p == NULL) { return 1; } //释放 free(p); p = NULL;//置为空指针后程序就不会崩溃了 free(p);//p为空指针时,程序不会报错 return 0; }
例5:
在实现函数时开辟了动态内存要记得及时释放或者返回地址,
不然就再也找不到那段内存空间了,最后导致内存泄漏。
#include <stdio.h> #include <stdlib.h> void test() { int* p = (int*)malloc(100); //忘记释放 }//出了函数就找不到了,因为变量p被销毁了 //造成内存泄漏 int main() { test(); return 0; }
例6:
这道题也是类似的:
#include <stdio.h> #include <stdlib.h> void test() { int* p = (int*)malloc(100); if (p == NULL) { return; } //使用 if (1) return;//出问题//内存泄漏 //释放 free(p); p = NULL; } int main() { test(); return 0; }
要小心出现内存泄漏,记得释放空间。
到此这篇关于C语言动态内存分配图文讲解的文章就介绍到这了,更多相关C语言动态内存分配内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!