动态内存malloc,calloc,realloc如何使用,使用场景及使用free释放内存时崩溃的原因

这篇具有很好参考价值的文章主要介绍了动态内存malloc,calloc,realloc如何使用,使用场景及使用free释放内存时崩溃的原因。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

1.内存区域

2.void与void*

3.应用场景

4.malloc

5.calloc

6.realloc

7.free崩溃的原因

7.1引入

7.2具体原因

7.2.1越界

7.2.2指针移动

7.2.3重复释放同一段内存


1.内存区域

局部变量:定义在函数内部的变量,包括形参,在栈(stack),作用域在函数内部有效,生存周期:进入函数创建,退出函数销毁。

栈:内存空间,局部变量所在的内存区域,系统自行管理内存的分配和回收,容量小(1M,不同的系统存在差异)
:内存空间,动态内存所在的内存区域,由程序(员)管理内存的分配和回收(释放),容量大(1G,不同的系统存在差异)

2.void与void*

void:没有,只能用于返回值或参数列表,表示无返回值或无参数(这个一般省略不写)
void a;错误的
void*:通用指针或泛型指针,没有具体的数据类型的指针,不能[],+i等运算,使用时需要强制类型转换

3.应用场景

1.需要根据变量作为长度定义数组。
2.函数结束后还需要继续使用的内存 ( 例如返回局部数组的地址 , 链表)
3.长度较大的数组 ( 大内存 , 超过栈 1M 的大小)

4.malloc

动态申请内存,需要引用stdlib.h,没有默认值,具体参考帮助手册。

应用场景1.

//1.需要根据变量作为长度定义数组
int main()
{
	int n = 10;
	//int arr[n];//error,变量不能作为数组的长度,C99合法
	int* p = (int*)malloc(n * sizeof(int));//创建成功后,p类似数组名
	assert(p != NULL);
	if (p == NULL)
	{
		perror("出错了");
		return 0;
	}
	for (int i = 0; i < n; i++)
		p[i] = i;
	for (int i = 0; i < n; i++)
		printf("%d ", p[i]);
	return 0;
}

应用场景2.

错误用法:

如下代码,如果返回值为数组,数组是局部变量,函数结束后,系统自行对变量进行回收,这时return str,返回变量的地址,地址仍存在,并没有销毁,所以在主函数中打印函数的返回值时,打印的是函数传的地址,a返回函数传的地址取字符串,此时字符串为随机值,函数内的数据已经随函数的结束而销毁。

char* Getstr()
{
	char str[] = "hello world";//函数结束后,系统自行对变量进行回收
	return str;//返回变量的地址,地址仍存在,并没有销毁
}

int main()
{
	char* a = Getstr();
	printf("%s", a);//乱码
	return 0;
}

辨析:返回值为普通变量时为什么不用动态内存?

如下代码,在函数中返回的是整型a的具体值,而不是它的地址。访问函数时直接将局部变量x赋值为a的值,不会再跳转到a的地址,再取a的值。

int Fun1() {
	int a = 10;
	return a;//合法,返回的是a的值,而不是地址
}

int main() {
	int x=Fun1();
	printf("%d ", x);
	return 0;
}

 正确写法如下,使用动态内存:

动态内存,由程序员自行销毁,可以用来函数传参,使用完成后再释放。

char* GetStr()
{
	int len = strlen("hello world");
	//char* str = (char*)malloc(len * sizeof(char));//常见错误
	char* str = (char*)malloc((len + 1) * sizeof(char));
	assert(str != NULL);
	strcpy(str, "hello world");
	return str;
}

int main()
{
	char* p = GetStr();
	printf("%s\n", p);
	free(p);
	return 0;
}
应用场景 3( 需要大容量的内存 ):
int main()
{
	//定义1000000长度的int数组
	//int arr[1000000];//不能定义这么大的数组
	//int* arr = (int*)malloc(1000000 * sizeof(int));//ok
	//char* arr = (char*)malloc(1024 * 1024 * 1024);//1G,ok
	char* arr = (char*)malloc(1024 * 1024 * 1020 * 2);//20亿字节,2G失败
	if (arr == NULL)
		perror("出错了");
	assert(arr != NULL);
	printf("好了\n");
	getchar();
	free(arr);
	return 0;
}

5.calloc

申请内存函数 , 把每个元素初始化为 0, 具体参考帮助手册
int main()
{
	int n = 10;
	int* arr = (int*)calloc(n, sizeof(int));
	for (int i = 0; i < n; i++)
		printf("%d ", arr[i]);//输出n个 0
	free(arr);
	return 0;
}

6.realloc

扩大内存函数,具体参考帮助手册 (通常情况,扩容后地址会改变而不是在原来的地址上进行扩容,与操作系统的决策有关)

int main()
{
   char *str;
 
   /* 最初的内存分配 */
   str = (char *) malloc(15);
   strcpy(str, "runoob");
   printf("String = %s,  Address = %p\n", str, str);
 
   /* 重新分配内存 */
   str = (char *) realloc(str, 25);
   strcat(str, ".com");
   printf("String = %s,  Address = %p\n", str, str);
 
   free(str);
   
   return(0);
}

7.free崩溃的原因

7.1引入

根据以往的编程经验,在使用函数传递数组时,形参包括数组的首地址arr数组的长度len,因为传递的是首地址,是一个指针,无法在函数内部根据sizeof(arr)/sizeof(数组类型)求得数组的长度,所以形参必须包括数组的长度。

但是在使用free()释放内存空间时,只传递了数组的首地址,并没有传递数组的长度,因为在申请动态内存时,这段内存的头尾会分别生成标记,标记也占一定的内存,所以不需要传长度信息,但是在操作时如果不小心破坏了这个标记,在释放内存时就会发生错误。

int main() {
	int n = 10;
	int* arr = (int*)malloc(n * sizeof(int));

	arr = (int*)realloc(arr, 2 * n * sizeof(int));//arr接收新的地址
	free(arr);//没有传长度,申请的内存头尾会有标记占一定的内存,不需要传长度信息

	return 0;
}

7.2具体原因

  • 越界
  • 指针移动
  • 重复释放同一段内存
  • 释放不是动态创建的内存

7.2.1越界

越界会损坏所申请空间的结尾标志。

//1.越界
int main()
{
	int n = 10;
	int* arr = (int*)malloc(n);
	assert(arr != NULL);
	for (int i = 0; i < n; i++)
		arr[i] = i;
	for (int i = 0; i < n; i++)
		printf("%d ", arr[i]);
	printf("\n");
	free(arr);
	return 0;
}
int main() {
	int n = 10;
	int* arr = (int*)malloc(n * sizeof(int));
	assert(arr != NULL);
	for (int i = 0; i <= n; i++)//越界,申请空间的损坏结尾标志
		arr[i] = 0;
	free(arr);//程序崩溃
	return 0;
}

7.2.2指针移动

指针为数组首地址,指针移动了,在释放时就找不到所申请空间的头部信息。文章来源地址https://www.toymoban.com/news/detail-743545.html

//指针移动
int main() {
	int n = 10;
	int* arr = (int*)malloc(10 * sizeof(int));
	for (int i = 0; i < n; i++) {
		*arr = 0;
		arr++;//指针移动了,释放时找不到头部信息
	}
	free(arr);//崩溃
	return 0;
}

7.2.3重复释放同一段内存

//重复释放同一段内存
int main()
{
	int n = 10;
	int* arr = (int*)malloc(n * sizeof(int));
	assert(arr != NULL);
	for (int i = 0; i < n; i++)
	{
		arr[i] = i;
	}
	printf("%p\n", arr);
	free(arr);
	printf("%p\n", arr);//arr是野指针
	free(NULL);//可以
	//free(arr);//崩溃,重复释放
	return 0;
}

到了这里,关于动态内存malloc,calloc,realloc如何使用,使用场景及使用free释放内存时崩溃的原因的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 动态内存函数malloc,calloc,realloc详解

    🍍个人主页🍍:🔜勇敢的小牛儿🚩 🔱推荐专栏🔱:C语言知识点 ⚠️座右铭⚠️: 敢于尝试才有机会 🐒今日鸡汤🐒: 出色一点 从能力到容貌 目录 思维导图: 一,malloc : 1.1:malloc函数简介: 1.2:malloc函数的使用:  代码: 二,calloc函数 2.1calloc函数简介: 2.2calloc函数

    2024年02月04日
    浏览(43)
  • 如何实现动态分配,malloc,realloc,calloc的使用方法,数组,链表,结构体实现动态分配(含代码实现)

    🎊【数据结构与算法】专题正在持续更新中,各种数据结构的创建原理与运用✨,经典算法的解析✨都在这儿,欢迎大家前往订阅本专题,获取更多详细信息哦🎏🎏🎏 🪔本系列专栏 -  数据结构与算法_勾栏听曲_0 🍻欢迎大家  🏹  点赞👍  评论📨  收藏⭐️ 📌个人主

    2023年04月27日
    浏览(43)
  • 动态内存分配:malloc、calloc、realloc(超详解析,多维度分析,小白一看就懂!!!!)

    目录 一、前言 二、动态内存的简单介绍 🍉什么是动态内存分配 🍎为什么要使用动态内存分配  三、动态内存函数的介绍和拓展  🍋malloc()函数  🍊free()函数  🍌calloc()函数 🍇realloc()函数 四、常见动态内存分配的错误 五、共勉 在学习 动态内存分配 时,感觉这些动态分

    2024年02月05日
    浏览(45)
  • calloc、malloc、realloc函数的区别及用法

    三者都是分配内存,都是stdlib.h库里的函数,但是也存在一些差异。 (1)malloc函数。其原型void *malloc(unsigned int num_bytes); num_byte为要申请的空间大小,需要我们手动的去计算,如int *p = (int )malloc(20 sizeof(int)),如果编译器默认int为4字节存储的话,那么计算结果是80Byte,一次申请

    2024年02月08日
    浏览(45)
  • 【C++】深入探讨内存管理:malloc/free与new/delete的区别以及如何避免内存泄漏

    在软件开发中,正确处理内存管理是至关重要的一环。在C++编程中,我们经常会用到动态内存管理的工具,比如 malloc/free 和 new/delete 。本文将深入探讨 malloc/free 与 new/delete 之间的区别,以及如何有效地避免内存泄漏问题。 都是用于从堆上申请空间,并需要手动释放。 mallo

    2024年02月22日
    浏览(54)
  • C语言 malloc动态内存分配函数

    malloc函数:malloc时动态内存分配函数,用于申请一块连续的指定大小的内存块区域以void*类型返回分配的内存区域地址,就是当数组创建长度不一定 害怕数据存储不够或者不能浪费时间 在使用malloc开辟空间时,使用完成一定要释放空间,如果不释放会造内存泄漏。n在使用ma

    2024年02月07日
    浏览(45)
  • C++与C语言动态内存管理的不同 new与malloc

      目录 1.C语言动态内存管理方式 2.C++中动态内存管理 2.1 new和delete操作内置类型 2.2 new和delete操作自定类型 2.3 为什么delete要带[ ]  3.new申请空间失败 4.operator new 与 operator delete 函数 5.new与delete的是实现原理 5.1 内置类型 5.2 自定义类型 6.定位new表达式(了解即可) 7.malloc/free和

    2024年02月08日
    浏览(41)
  • 【C语言】free()函数详解(动态内存释放函数)

    🦄 个人主页 :修修修也 🎏 所属专栏 :C语言 ⚙️ 操作环境 : Visual Studio 2022 目录  一.free()函数简介 1.函数功能 2.函数参数 void * ptr 3.函数返回值 4.函数头文件 二.free()函数的具体使用 1.使用free()函数完成malloc()开辟空间的释放 2.使用free()函数完成calloc()开辟空间的释放 3.使用

    2024年02月08日
    浏览(38)
  • new/delete与malloc/free的区别

    new、delete是C++中的操作符,而malloc、free是标准库函数。 new 和 delete 是类型安全的,它们能够根据要分配的对象类型进行内存分配和释放,并调用相应的构造函数和析构函数。而 malloc 和 free 则是无类型的,它们只关注分配和释放指定大小的内存块。 new 操作符会自动计算要分

    2024年02月10日
    浏览(41)
  • android 如何分析应用的内存(八)——Android 7.0以后的malloc debug

    接上文,介绍六大板块中的第三个————malloc调试和libc回调 上一篇文章中,仅仅是在分配和释放的时候,拦截对应的操作。而不能进一步的去检查内存问题。比如:释放之后再次使用指针,内存泄漏,内存损坏等等。 在这篇文章中,将会介绍malloc调试技术,它可以对nat

    2024年02月10日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包