目录
今天收假了,抓紧时间写几篇博客。我又来赶进度了。今天我们来讲解动态内存管理。🆗🆗
假设我们去实现一个通讯录,我们设置通讯录的大小是固定的100个元素,存放100个人的信息。如果信息太多,空间小了。如果信息太少,空间又大了。那我们应该怎样去解决?动态内存管理!
在目前为止,我们已经掌握两种向栈区申请内存的方式。
- #include
- int main()
- {
- int a = 10;//在栈空间申请四个字节存放一个值
- int arr[] = { 1,2,3,4,5,6,7,8,9,10 };//在栈空间开辟连续的空间存放一组数
- return 0;
- }
但是上诉的开辟空间的方式有两个特点:
但是对于空间的需求,不仅仅是上诉的情况。有时候我们需要的空间大小在程序运行的时候才能知道那数组的编译时开辟空间的方式就不能满足了。 这时候C语言给程序员一种权利【能够动态的申请和管理内存空间】就是【动态内存开辟】,当然除了在申请的同时 我们也要学会释放空间。
当然我们头脑中还是要有【内存分布图】
接下里我们分别给大家详细介绍一下动态内存开辟的函数 我们将从:头文件 函数参数 返回值 使用等方面去介绍。大家认真学起来!!
malloc - C++ Reference (cplusplus.com)
【malloc的使用需要注意:强制类型转化&&判断&&需要free释放】
- //假设现在程序员A想申请40个字节的空间去存放10个整型
- #include
- #include
- #include
- int main()
- {
- int* p = (int*)malloc(10 * sizeof(int));//强制类型转化
- //开始存放
- if (p == NULL)
- {
- perror("malloc");//为空的原因
- return 1;//非0即不能正常返回
- }
- int i = 0;
- for (i = 0; i < 10; i++)
- {
- p[i] = 1+i;//p相当于数组名
- //p+i=i+1;
- }
- //打印
- for (i = 0; i < 10; i++)
- {
- printf("%d ", p[i]);//p+i
- }
- //释放
- free(p);
- p = NULL;
- return 0;//可以正常返回
- }
忘记【perror库函数】戳一戳:C语言之字符函数&字符串函数篇(2)_唐唐思的博客-CSDN博客
当然如果申请的空间太大,也是不可以的!
- #include
- #include
- #include
- int main()
- {
- int* p = (int*)malloc(INT_MAX*4);//强制类型转化
- //这里的空间过大会返回NULL的
- if (p == NULL)
- {
- perror("malloc");//为空的原因
- return 1;//非0即不能正常返回
- }
- return 0;
- }
【malloc函数申请的空间是需要释放的】要怎么释放呢?
free - C++ Reference (cplusplus.com)
- free(p);
- p = NULL;//p是接收malloc开辟的空间的起始地址的指针变量
- #include
- int main()
- {
- //代码1
- int num = 0;
- scanf("%d", &num);
- int arr[num] = { 0 };
- //代码2
- int* ptr = NULL;
- ptr = (int*)malloc(num * sizeof(int));
- if (NULL != ptr)//判断ptr指针是否为空
- {
- int i = 0;
- for (i = 0; i < num; i++)
- {
- *(ptr + i) = 0;
- }
- }
- free(ptr);//释放ptr所指向的动态内存
- ptr = NULL;//是否有必要?
- return 0;
- }
calloc - C++ Reference (cplusplus.com)
- //向堆区申请10个整型的空间
- calloc(10, sizeof(int));
- malloc(10 * sizeof(int));
除了参数的区别,calloc函数申请好空间后【会将空间初始化为0】但是malloc函数不会初始化。
- #include
- #include
- int main()
- {
- int* p = (int*)calloc(10,sizeof(int));//强制类型转化
- int* p = (int*)malloc(10*sizeof(int));//强制类型转化
- //开始存放
- if (p == NULL)
- {
- perror("malloc");//为空的原因
- return 1;//非0即不能正常返回
- }
- int i = 0;
- //打印
- for (i = 0; i < 10; i++)
- {
- printf("%d\n", p[i]);//p+i
- }
- //释放
- free(p);
- p = NULL;
- return 0;//可以正常返回
- }
malloc打印出来的是随机值,而calloc打印出来是初始化为0的值。 根据需求使用,如果需要初始化为0,那我们可以使用【calloc】,如果不需要初始化为0,我们可以使用【malloc】
有时会我们发现过去申请的空间太小了,有时候我们又会觉得申请的空间过大了,那为了合理的时候内存,我们一定会对内存的大小做灵活的调整。那 realloc 函数就可以做到对动态开辟内存大小的调整。 realloc - C++ Reference (cplusplus.com)
1.可能与旧空间的起始地址一致 2.可能是一块全新的空间的起始地址
关于realloc函数有返回值来接收有三种不同写法。
【写法1】
p = realloc(p, 20 * sizeof(int));//用旧空间的原来的指针变量去接收(NULL问题)
- #include
- #include
- int main()
- {
- int* p = (int*)calloc(10,sizeof(int));//强制类型转化
- //开始存放
- if (p == NULL)
- {
- perror("malloc");//为空的原因
- return 1;//非0即不能正常返回
- }
- int i = 0;
- //打印
- for (i = 0; i < 10; i++)
- {
- printf("%d\n", p[i]);
- }
- //空间不够,希望调整空间为20个整型的空间
- p = realloc(p, 20 * sizeof(int));
- //不建议这样写 可能开辟空间失败返回NULL
- // 成功也就罢了,万一失败旧空间起始地址也找不到了
- //释放
- free(p);
- p = NULL;
- return 0;//可以正常返回
- }
【写法2】
int* ptr = (int*)realloc(p, 20 * sizeof(int));//用新的指针变量去接收,但记住一定要释放
- #include
- #include
- int main()
- {
- int* p = (int*)calloc(10,sizeof(int));//强制类型转化
- //开始存放
- if (p == NULL)
- {
- perror("malloc");//为空的原因
- return 1;//非0即不能正常返回
- }
- int i = 0;
- //打印
- for (i = 0; i < 10; i++)
- {
- printf("%d\n", p[i]);
- }
- //空间不够,希望调整空间为20个整型的空间
- int *ptr = (int*)realloc(p, 20 * sizeof(int));//换一个指针变量去管理
- //如果你就是使用ptr,一定记得要释放ptr所指向的空间
- //释放
- free(p);
- free(ptr);
- p = NULL;
- ptr=NULL;
- return 0;//可以正常返回
- }
【写法3】
int* ptr = (int*)realloc(p, 20 * sizeof(int));
if (ptr != NULL)
{
p = ptr;
}
- #include
- #include
- int main()
- {
- int* p = (int*)calloc(10,sizeof(int));//强制类型转化
- //开始存放
- if (p == NULL)
- {
- perror("malloc");//为空的原因
- return 1;//非0即不能正常返回
- }
- int i = 0;
- //打印
- for (i = 0; i < 10; i++)
- {
- printf("%d\n", p[i]);
- }
- //空间不够,希望调整空间为20个整型的空间
- int *ptr = (int*)realloc(p, 20 * sizeof(int));
- //但是,程序员还是想要p来管理这块空间,可以这么写
- if (ptr != NULL)
- {
- p = ptr;
- }
- //释放
- free(p);
- p = NULL;
- return 0;//可以正常返回
- }
关于就是realloc函数返回的指针的两种不同的情况。
【情况1 】
要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发生变化。
【情况2】
原有空间之后没有足够多的空间时,扩展的方法是:在堆空间上另找一个合适大小的连续空间来使用。这样函数返回的是一个新的内存地址。 且realloc函数有三个特点
关于原地和异地扩容的验证:
【原地扩容】
- #include
- #include
- int main()
- {
- int* p1 = (int*)malloc(40);
- int* p2 = (int*)realloc(p1,80);//扩容40
- printf("p1=%p\n", p1);
- printf("p2=%p", p2);
- return 0;
- }
【异地扩容】
- #include
- #include
- int main()
- {
- int* p1 = (int*)malloc(40);
- int* p2 = (int*)realloc(p1,800);//扩容760
- printf("p1=%p\n", p1);
- printf("p2=%p", p2);
- return 0;
- }
当然除此之外,realloc还可以当成malloc来使用,只要传空指针即可。
所以所以 ptr可以为空指针NULL
- #include
- #include
- int main()
- {
- int* ptr = (int*)realloc(NULL, 10 * sizeof(int));
- if (ptr == NULL)
- {
- perror("realloc");
- return 1;
- }
- free(ptr);
- ptr = NULL;
- return 0;
- }
接下来我们一个一个纠错!
- //对NULL指针的解引用操作
- void test()
- {
- int* p = (int*)malloc(40);
- //不做返回值的判断,就可能是使用空指针解引用
- *p = 20;//如果p的值是NULL,就会有问题
- free(p);
- }
【修改】加上返回值的判断
- #include
- void test()
- {
- int* p = (int*)malloc(40);
- if (p == NULL)
- {
- perror("malloc");
- return 1;
- }
- *p = 20;
- free(p);
- }
- int main()
- {
- test();
- return 0;
- }
- //对动态开辟空间的越界访问
- #include
- void test()
- {
- int i = 0;
- int* p = (int*)malloc(10 * sizeof(int));
- if (NULL == p)
- {
- perror("malloc");
- return 1;
- }
- for (i = 0; i <= 10; i++)
- {
- *(p + i) = i;//当i是10的时候越界访问
- }
- free(p);
- p = NULL;
- }
- int main()
- {
- test();
- return 0;
- }
【修改】不越界即可
- #include
- void test()
- {
- int i = 0;
- int* p = (int*)malloc(10 * sizeof(int));
- if (NULL == p)
- {
- perror("malloc");
- return 1;
- }
- for (i = 0; i < 10; i++)
- {
- *(p + i) = i;//当i是10的时候越界访问
- }
- free(p);
- p = NULL;
- }
- int main()
- {
- test();
- return 0;
- }
- //对非动态开辟内存使用free释放
- #include
- void test()
- {
- int a = 10;
- int* p = &a;
- free(p);
- p = NULL;//err
- }
- int main()
- {
- test();
- return 0;
- }
【修改】不可以哈,删去free
- //对非动态开辟内存使用free释放
- #include
- void test()
- {
- int a = 10;
- int* p = &a;
- }
- int main()
- {
- test();
- return 0;
- }
- //使用free释放一块动态开辟内存的一部分
- //示例1
- #include
- void test()
- {
- int* p = (int*)malloc(100);
- p++;
- free(p);//p不再指向动态内存的起始位置
- }
- int main()
- {
- test();
- return 0;
- }
-
- //示例2
- #include
- #include
- int main()
- {
- int* p = (int*)calloc(10, sizeof(int));
- if (p == NULL)
- {
- perror("calloc");
- return 1;
- }
- //赋值
- int i = 0;
- for (i = 0; i < 5; i++)
- {
- *p = i;
- p++;
- }
- //0 1 2 3 4 0 0 0 0 0
- for (i = 0; i < 5; i++)
- {
- printf("%d ", p[i]);
- }
- free(p);//只是释放了后面五个0
- p = NULL;
- return 0;
- }
【修改】不要让p移动,想移动就另外设置一个指针让它去移动。养成好习惯不要动起始的指针
- //示例1
- #include
- #include
- void test()
- {
- int* p = (int*)malloc(100);
- int* ps = NULL;
- if (p == NULL)
- {
- perror("malloc");
- return 1;
- }
- else
- {
- ps = p;
- }
- ps++;
- free(p);
- p = NULL;//p不再指向动态内存的起始位置
- }
- int main()
- {
- test();
- return 0;
- }
-
- //示例2
-
- #include
- #include
- int main()
- {
- int* p = (int*)calloc(10, sizeof(int));
- if (p == NULL)
- {
- perror("calloc");
- return 1;
- }
- //赋值
- int i = 0;
- for (i = 0; i < 5; i++)
- {
- p[i] = i;
- }
- //0 1 2 3 4 0 0 0 0 0
- for (i = 0; i < 5; i++)
- {
- printf("%d ", p[i]);
- }
- free(p);//只是释放了后面五个0
- p = NULL;
- return 0;
- }
- //对同一块动态内存多次释放
- #include
- #include
- void test()
- {
- int* p = (int*)malloc(100);
- free(p);
- free(p);//重复释放
- }
- int main()
- {
- test();
- return 0;
- }
【修改】不要多次释放
- #include
- #include
- void test()
- {
- int* p = (int*)malloc(100);
- free(p);
- }
- int main()
- {
- test();
- return 0;
- }
- #include
- #include
- void test()
- {
- int* p = (int*)malloc(100);
- free(p);
- p = NULL;
- free(p);//ok
- }
- int main()
- {
- test();
- return 0;
- }
- //动态开辟内存忘记释放(内存泄露)
- //示例1
- #include
- #include
- void test()
- {
- int* p = (int*)malloc(100);
- if (NULL != p)
- {
- *p = 20;
- }
- }
- int main()
- {
- test();
- while (1);//忘记释放
- }
- //示例2
- #include
- #include
- void test()
- {
- int* p = (int*)malloc(100);
- if (NULL != p)//这里直接跳出循环
- {
- *p = 20;
- }
- free(p);//无用
- p = NULL;
- }
- int main()
- {
- test();//调用完成 malloc申请的空间还在
- //没有忘记释放。但是释放没有用
- while (1);//死循环程序退出不了
- }
【修改】 忘记释放或在函数内部释放了但是没有使用都会造成内存泄露
- #include
- #include
- int* test()
- {
- int* p = (int*)malloc(100);
- if (NULL != p)
- {
- *p = 20;
- }
- return p;
- }
- int main()
- {
- int *p=test();
- free(p);
- p = NULL;
- while (1);//忘记释放
- }
动态开辟的空间一定要正确释放!!
✔✔✔✔✔最后,感谢大家的阅读,若有错误和不足,欢迎指正!下篇博文我们讲解几道相关笔试题
代码------→【gitee:唐棣棣 (TSQXG) - Gitee.com】
联系------→【邮箱:2784139418@qq.com】