动态内存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,okchar* 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个 0free(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指针移动

指针为数组首地址,指针移动了,在释放时就找不到所申请空间的头部信息。

//指针移动
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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/130048.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【缓存】Spring全家桶中@CacheEvict无效情况共有以下几种

Spring全家桶中CacheEvict无效情况共有以下几种 一、背景介绍二、原因分析三、解决方案 一、背景介绍 SpringBoot中使用Cacheable注解缓存数据&#xff0c;使用CacheEvict注解删除缓存。但是在项目使用过程中&#xff0c;发现使用CacheEvict注解删除缓存无效。 拓展&#xff…

Redis那些事儿(一)

说到redis大家都不陌生&#xff0c;其中包括&#xff1a;共有16个数据库&#xff0c;默认为第0个数据库&#xff1b;数据以key-value键值的形式存储&#xff1b;数据类型包括String、List、Hash、Set等&#xff0c;其中最常用的是字符串&#xff1b;是单线程的、基于内存的&…

腾讯云优惠券是什么?腾讯云优惠券怎么领取?

腾讯云是腾讯集团倾力打造的云计算品牌&#xff0c;为了吸引用户上云&#xff0c;经常推出各种优惠活动&#xff0c;其中就包括腾讯云优惠券。 1、腾讯云优惠券解释说明 腾讯云优惠券是腾讯云的一种优惠凭证&#xff0c;包括代金券和折扣券&#xff0c;领券之后新购、续费、升…

AD9371 官方例程裸机SW 和 HDL配置概述(三)

AD9371 系列快速入口 AD9371ZCU102 移植到 ZCU106 &#xff1a; AD9371 官方例程构建及单音信号收发 ad9371_tx_jesd -->util_ad9371_xcvr接口映射&#xff1a; AD9371 官方例程之 tx_jesd 与 xcvr接口映射 AD9371 官方例程 时钟间的关系与生成 &#xff1a; AD9371 官方…

前端之jQuery

目录 一、jQuery jQuery内容 jQuery版本 jQuery对象 jQuery基础语法 二、查找标签 基本选择器 层级选择器 基本筛选器 属性选择器 表单筛选器 三、筛选器方法 四、操作标签 一、jQuery jQuery是一个轻量级的、兼容多浏览器的JavaScript库。jQuery使用户能够更方便…

管理如何实现制度流程化 流程表单化 表单信息化 信息标准化?

业务化、流程化、信息化、数字化、自动化、智能化&#xff0c;是企业业务管理发展路径的六个必经阶段 制度业务化&#xff0c;业务表单化&#xff0c;表单流程化、流程信息化、信息标准化、标准制度华。 制度流程化、流程表单化、表单信息化、信息标准化、标准制度华。 管理…

Windows安装WinDbg调试工具

一.下载 微软官网下载SDK的地址&#xff0c;有win11&#xff0c;win10&#xff0c;win8&#xff0c;win7&#xff0c;其他 https://developer.microsoft.com/en-us/windows/downloads/sdk-archive/ 二.安装 打开windbg\Installers\X64 Debuggers And Tools-x64_en-us.msi 要安…

《Effective C++》知识点(6)--继承与面向对象设计

32. 确定你的public继承模式是is-a关系 重要规则&#xff1a;public继承就意味is-a的关系。适用于基类身上的每一件事情一定也适用于继承类身 上&#xff0c;因为每一个继承类对象也都是一个基类对象。 另外两种关系是has-a(有一个)和is-implemented-in-terms-of(根据某物实现出…

[揭秘]splunk 背后running 的job

1: 背景: 最近客户总是会收到一些dashboard 的PDF 文件,有些内容不是想要的,想要求更新一下,但是问题来了,这些dashboard 的自动运行的job 在哪里呢? 2: 查找原因: 2.1 如果知道这个dashboard 发到那个email 的,那么可以查找: abc@163.com 去: /opt/splunk/etc/use…

【Redis】String字符串类型-常用命令

文章目录 String字符串类型常用命令setgetMGETMSET 计数命令INCRINCRBYDECRDECRBYINCRBYFLOAT 其它命令APPENDGETRANGESETRANGESTRLEN 命令总结 String字符串类型 1&#xff09;⾸先Redis中所有的键的类型都是字符串类型 2&#xff09;字符串类型的值&#xff08;value&#…

数据结构详细笔记——并查集

文章目录 逻辑结构存储结构并、查代码实现Union 操作的优化Find 操作的优化&#xff08;压缩路径&#xff09; 逻辑结构 集合&#xff1a;将各个元素划分为若干个互不相交的子集的集合 森林是m(m>0)棵互不相交的树的集合 存储结构 #define SIZE 13 int UFSets[SIZE]; …

linux环境下编译,安卓平台使用的luajit库

一、下载luajit源码 1、linux下直接下载&#xff1a; a、使用curl下载&#xff1a;https://luajit.org/download/LuaJIT-2.1.0-beta3.tar.gz b、git下载地址&#xff1b;https://github.com/LuaJIT/LuaJIT.git 2、Windows下载好zip文件&#xff0c;下载地址&#xff1a;https…

云计算、大数据、人工智能、物联网、虚拟现实技术、区块链技术(新一代信息技术)学习这一篇够了!

目录 云计算 一、云计算的基本概念 二、云计算的分类 (一) IaaS (二) SaaS (三) PaaS 三、云环境的分类、云计算的四种部署模式 (一)公有云 (二)私有云 (三)社区云 (四)混合云 四、云计算的特点 (一)虚拟化技术 (二)动态可扩展 (三)按需部署 (四)灵活性高 (五…

左偏树学习笔记

定义 堆&#xff0c;是一棵树&#xff0c;且每个节点的键值都大于等于 / 小于其父亲的键值。 左偏树是一种可合并的堆&#xff0c;可以以 O ( log ⁡ n ) O(\log n) O(logn) 的复杂度实现合并。 性质 左偏树满足堆的性质。 我们设定一个值 dist \text{dist} dist&#xf…

动手学深度学习:1.线性回归从0开始实现

动手学深度学习&#xff1a;1.线性回归从0开始实现 1.手动构造数据集2.小批量读取数据集3.初始化模型参数4.定义模型和损失函数5.小批量随机梯度下降更新6.训练完整代码 1.手动构造数据集 根据带有噪声的线性模型构造一个人造数据集&#xff0c;任务是使用这个有限样本的数据集…

692. 前k个高频单词(map)

目录 一、题目 二、代码 一、题目 692. 前K个高频单词 - 力扣&#xff08;LeetCode&#xff09; 二、代码 class Solution {class KV_sort{public:bool operator()(const pair<string,int> kv1, const pair<string,int> kv2){if(kv1.first kv2.first )//两个对…

C51--项目--感应开关盖垃圾桶

1、项目概述 功能描述&#xff1a; 检测靠近时&#xff0c;垃圾桶自动开盖并伴随滴一声&#xff0c;2s后关盖。 发生震动时&#xff0c;垃圾桶自动开盖并伴随滴一声&#xff0c;2s后关盖。 按下按键时&#xff0c;垃圾桶自动开盖并伴随滴一声&#xff0c;2s后关盖。 硬件说明…

Flutter笔记:发布一个模块 scale_design - (移动端)设计师尺寸适配工具

Flutter笔记 发布一个模块scale_design设计师尺寸适配工具与常用组件库 作者&#xff1a;李俊才 &#xff08;jcLee95&#xff09;&#xff1a;https://blog.csdn.net/qq_28550263 邮箱 &#xff1a;291148484163.com 本文地址&#xff1a;https://blog.csdn.net/qq_28550263/a…

UE5C++学习(一)--- 增强输入系统

一、关于增强输入系统的介绍 增强输入系统官方文档介绍 二、增强输入系统的具体使用 注&#xff1a;在使用方面&#xff0c;不会介绍如何创建项目等基础操作&#xff0c;如果还没有UE的使用基础&#xff0c;可以参考一下我之前UE4的文章&#xff0c;操作差别不会很大。 如上…

HIT_OS_LAB2 调试分析 Linux 0.00 多任务切换

操作系统实验二 2.1 实验目的 通过调试一个简单的多任务内核实例&#xff0c;使大家可以熟练的掌握调试系统内核的方法&#xff1b;掌握Bochs虚拟机的调试技巧&#xff1b;通过调试和记录&#xff0c;理解操作系统及应用程序在内存中是如何进行分配与管理的&#xff1b; 2.2…