当前位置: 首页 > news >正文

c语言—动态内存管理

一.为什么存在动态内存开辟

开辟空间的特点:

  1. 空间开辟大小是固定的

  1. 数组在申明时,必须指定数组长度,她所需要的内存在编译时分配

但是对于空间的需求,不仅仅是上述的情况。有时候我们需要的空间大小在程序运行的时候才能知道,

那数组的编译时开辟空间的方式就不能满足了。

这时候就只能试试动态存开辟了

二.动态内存函数的介绍

1.malloc和free

void*malloc(size_t size);

这个函数向内存申请一块连续的内存空间,并返回这块空间的指针。

  • 如果开辟成功,则返回一个指向这块空间的指针

  • 如果开辟失败,这返回一个NULL,因此malloc函数的返回值一定要做检查

  • 返回的类型是void*,所以malloc函数并不知到开辟空间的类型,具体在使用时使用者自己决定

  • 如果参数 size 为0,malloc的行为是标准是未定义的,取决于编译器

free函数使用来释放动态开辟的内存的

void free(void*ptr)
  • 如果参数ptr指向的空间不是动态内存开辟的,那么free函数的行为是未定义的

  • 如果ptr是NULL,则free函数什么都不做

注:malloc和free都声明在stdlib.h的文件中

例:

#include<stdio.h>int main()
{int num=0;scanf("%d",&num);int arr[num]=0;//变长数组int*ptr=NULL;ptr=(int*)malloc(num*sizeof(int));if(NULL!=ptr)//判读ptr是否为空指针{int i=0;for(i=0;i<num;i++){*(ptr+i)=i;}}free(ptr);//释放ptr所指向的动态内存ptr=NULL;//是否有必要?return 0;
}

虽然程序结束后被开辟的内存空间会被释放,但是这样浪费的很多空间,而free能够充分利用内存空间。

最后虽然free(ptr)释放了开辟的动态空间,但是ptr仍然指向那个空间的地址,所以要将ptr设置为空指针来避免空间被不会好意的人通过ptr找到。

2.calloc

void*callor(size_t num,size_t size)
  • 函数的功能是为 num 个大小为 size 的元素开辟一块空间,并且把空间的每个字节初始化为0

  • 与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全0。

例:

#include<stdio.h>
#include<stdlib.h>
int main()
{int* p = calloc(10, sizeof(int));if(NULL != p){//使用空间}free(p);p = NULL;return 0;
}

3.realloc

  • realloc函数的出现让动态内存管理更加灵活

  • realloc 函数就可以做到对动态开辟内存大小

的调整。

void*realloc(void*str,size_t size);
  • ptr是要调整的内存地址

  • size是调整之后的内存大小

  • 返回调整之后内存的起始位置

  • 这个函数在调整原内存空间的大小的基础上,还会将原内存的数据移到新的空间里

  • realloc在调整内存空间时有两种情况

情况1:原有空间之后有足够大的空间

情况2:原有空间之后没有足够大的空间

当是情况1的时候,要扩展的内存就在原有的内存之后追加新的空间,原来的数据不发生改变。

当是情况2的时候,扩展的方式就是在堆空间上另找一个适合大小的连续空间使用。这样函数返回的是一个新的内存地址。

例:

int main()
{int* ptr = (int*)malloc(100);if (NULL == ptr){perror(ptr);}else{int i = 0;for(i=0;i<100;i++){*(ptr + i) = i;}}//扩展容量ptr = (int*)realioc(ptr, 1000);//这样可以吗?(如果申请失败会如何?)if (NULL == ptr){perror(ptr);}else{int i = 0;for(i=100;i<=1000;i++){*(ptr + i) = i;}free(ptr);ptr = NULL;return 0;
}

不能像代码1一样操作,因为若(int*)realloc(ptr, 1000)中realloc开辟失败,返回的空指针赋给了ptr,

那么ptr被改掉,ptr指向的空间就找不到了

修改后:

int main()
{int* ptr = (int*)malloc(100);if (NULL == ptr){perror(ptr);}else{int i = 0;for (i = 0; i < 100; i++){*(ptr + i) = i;}}//扩展容量int* p = NULL;p=realloc(ptr, 1000);if (p == NULL){perror(p);}else{ptr = p;int i = 0;for (i = 100; i <= 1000; i++){*(ptr + i) = i;}}free(ptr);ptr = NULL;return 0;
}

三.常见的动态内存错误

1.对空指针的解引用操作

void test()
{int* p = (int*)malloc(INT_MAX);*p = 20;//如果p是空指针就会有问题free(p);
}

2.对动态开辟空间的越界访问

void test()
{int i = 0;int* p = (int*)malloc(10 * sizeof(int));if (NULL == p){exit(EXIT_FAILURE);}for (i = 0; i <= 10; i++){*(p + i) = i;//当i是10的时候越界访问}free(p);
}

3.对非动态开辟内存使用free释放

void test()
{int a = 10;int* p = &a;free(p);
}

4.使用free释放动态开辟内存的一部分

void test()
{int* p = (int*)malloc(10 * sizeof(int));p++;free(p);//p不再指向动态内存的起始位置
}

5.对同一块内存多次释放

void test()
{int *p = (int *)malloc(100);free(p);free(p);//重复释放
}

6.动态内存开辟忘记释放(内存泄漏)

void test()
{int *p = (int *)malloc(100);if(NULL != p){*p = 20;}
}
int main()
{test();while(1);
}

注:忘记释放不再使用的动态开辟的空间会造成内存泄漏

四.几个经典的笔试题

题目1.

void GetMemory(char *p)
{p = (char *)malloc(100);
}
void Test(void)
{char *str = NULL;GetMemory(str);strcpy(str, "hello world");printf(str);
}

请问运行Test 函数会有什么样的结果?

结果:

  1. 运行代码会出现兵溃的现象

  1. 程序存在内存泄漏

原因是str以值传递的形式给p

p是GetMemory函数的形参,只能在函数内部有效

等GetMemory函数返回返回之后,动态开辟内存尚未释放

并且无法找到,所以会造成内存泄漏

改进:

法一:

可以传str的指针给GetMemory函数,*p就是str

在使用完str后释放它指向的内存空间并将str置为NULL

void GetMemory(char **p)
{*p = (char *)malloc(100);
}
void Test(void)
{char *str = NULL;GetMemory(&str);strcpy(str, "hello world");printf(str);free(str);str=NULL;
}int main()
{test();return 0;
}

法二:

将GetMemory的返回类型改为char*,返回指向开辟空间的地址

在使用完str后释放它指向的内存空间并将str置为NULL

char* GetMemory(char *p)
{p = (char *)malloc(100);return p;
}
void Test(void)
{char *str = NULL;str=GetMemory(&str);strcpy(str, "hello world");printf(str);free(str);str=NULL;
}int main()
{test();return 0;
}

题目2.

char *GetMemory(void)
{char p[] = "hello world";return p;
}
void Test(void)
{char *str = NULL;str = GetMemory();printf(str);
}int main()
{test();return 0;
}

请问运行Test 函数会有什么样的结果?

结果:打印的是随机值

相关文章:

c语言—动态内存管理

一.为什么存在动态内存开辟开辟空间的特点&#xff1a;空间开辟大小是固定的数组在申明时&#xff0c;必须指定数组长度&#xff0c;她所需要的内存在编译时分配但是对于空间的需求&#xff0c;不仅仅是上述的情况。有时候我们需要的空间大小在程序运行的时候才能知道&#xff…...

请说明Ajax、Fetch、Axios三者的区别

相同点&#xff1a; 1、三者都用于网络请求&#xff0c;但是不同维度 2、 Ajax(Asynchronous Javascript and XML)&#xff0c;一种技术的统称&#xff0c;并不是实际的API 3、Fetch是一个具体的API&#xff0c;浏览器里面直接有一个API就叫Fetch 4、 Axios是一个第三方库&…...

阿里p8测试总监,让我们用这份《测试用例规范》,再也没加班过

经常看到无论是刚入职场的新人&#xff0c;还是工作了一段时间的老人&#xff0c;都会对编写测试用例感到困扰&#xff1f;例如&#xff1a; 固然&#xff0c;编写一份好的测试用例需要&#xff1a;充分的需求分析能力 理论及经验加持&#xff0c;作为测试职场摸爬打滚的老人&…...

【Unity】数据持久化路径Application.persistentDataPath

今天突然想到这个路径Application.persistentDataPath&#xff0c;热更的重要路径&#xff0c;该文件夹可读可写&#xff0c;在移动端唯一一个可读写操作的文件夹。移动端可以将本地的资源&#xff08;资源MD5值配置表&#xff09;等一些文件放到StreamingAssets文件夹下&#…...

华为OD机试 - 插队(Java JS Python)

题目描述 某银行将客户分为了若干个优先级, 1 级最高, 5 级最低,当你需要在银行办理业务时,优先级高的人随时可以插队到优先级低的人的前面。 现在给出一个人员到来和银行办理业务的时间序列,请你在每次银行办理业务时输出客户的编号。 如果同时有多位优先级相同且最高…...

MongoDB数据库从入门到精通系列之八:调整oplog大小

MongoDB数据库从入门到精通系列之八:调整oplog大小 一、oplog的概念二、oplog大小三、调整oplog大小详细步骤一、oplog的概念 操作日志oplog包含了主节点执行的每一次写操作。oplog是存在于主节点local数据库中的一个固定集合。从节点通过查询此集合以获取需要复制的操作。每个…...

PCL 间接平差法拟合二维直线

目录 一、算法原理二、代码实现三、结果展示四、相关链接一、算法原理 通过传统最小二乘法对点云数据进行二维直线拟合时,可将误差只归因于一个方向上,本文假设误差只存在于 y y y轴方向上,设点云拟合的二维直线方程为: y =...

进程调度的基本过程

这里写目录标题什么是进程进程管理结构体或类的主要属性pid内存指针文件描述符表辅助进程调度的属性并发并行并发什么是进程 进程是操作系统对一个正在运行的程序的一种抽象&#xff0c;也就是说&#xff0c;一个运行起来的程序就是一个进程。 进程又是操作系统进行资源分配的…...

python自动化办公(二)

上接python自动化办公&#xff08;一&#xff09; 文章目录文件和目录操作使用shutil库文件查找globfnmatchhashlib文件和目录操作 使用shutil库 shutil库也是Python标准库&#xff0c;它可以处理文件、文件夹、压缩包&#xff0c;能实现文件复制、移动、压缩、解压缩等功能。…...

Qt Quick - GridLayout 网格布局

GridLayout 理论总结一、概述二、依赖属性三、例子1. 不含跨行的2. 带跨行列的3. 从右到左一、概述 GridLayout 是最常用的布局器&#xff0c;也叫网格布局器&#xff0c;如果网格布局被调整大小&#xff0c;布局中的所有 Item 将被重新排列。它类似于基于widget的QGridLayout…...

安卓手机也可以使用新必应NewBing

没有魔法安卓手机也可以使用新必应NewBing 目前知道的是安卓手机 安卓手机先安装一个猴狐浏览器 打开手机自带浏览器&#xff0c;搜索关键词&#xff1a;猴狐浏览器&#xff0c;找到官网 也可以直接复制这个网址 狐猴浏览器 lemurbrowser CoolAPK 我的手机是荣耀安卓手机…...

支付系统设计:消息重试组件封装

文章目录前言一、重试场景分析一、如何实现重试1. 扫表2. 基于中间件自身特性3. 基于框架4. 根据公司业务特性自己实现的重试二、重试组件封装1. 需求分析2. 模块设计2.1 持久化模块1. 表定义2. 持久化接口定义3. 持久化配置类2.2 重试模块1.启动2.重试3. 业务端使用1. 引入依赖…...

Visual Studio 2022 c#中很实用的VS默认快捷键和原生功能

常常使用VS感觉还是有必要掌握其默认的快捷键&#xff0c;我这个人比较懒&#xff0c;不喜欢动不动就去设置快捷键&#xff0c;系统有就用&#xff0c;记住了就可以到处用&#xff0c;问题是像我们这种有很多个工作场所的人不可能每台电脑都去配置一下快键键。实际上我使用3dma…...

Python的30个编程技巧

1. 原地交换两个数字 Python 提供了一个直观的在一行代码中赋值与交换&#xff08;变量值&#xff09;的方法&#xff0c;请参见下面的示例&#xff1a; x,y 10,20 print(x,y) x,y y,x print(x,y) #1 (10, 20) #2 (20, 10) 赋值的右侧形成了一个新的元组&#xff0c;左侧立即解…...

MySQL:JDBC

什么是JDBC&#xff1f; JDBC( Java DataBase Connectivity ) 称为 Java数据库连接 &#xff0c;它是一种用于数据库访问的应用程序 API &#xff0c;由一组用Java语言编写的类和接口组成&#xff0c;有了JDBC就可以 用统一的语法对多种关系数据库进行访问&#xff0c;而不用担…...

C++【list容器模拟实现函数解析】

list容器&&模拟实现函数解析 文章目录list容器&&模拟实现函数解析一、list容器使用介绍二、list容器模拟实现及函数解析2.1 list结构体创建2.2 迭代器封装2.21 构造函数&#xff1a;2.22 前置和后置及- -2.23 解引用2.24 判断相等2.25 箭头重载2.26 第二个和第…...

(Java)试题 算法提高 约数个数

一、题目 &#xff08;1&#xff09;资源限制 内存限制&#xff1a;512.0MB C/C时间限制&#xff1a;1.0s Java时间限制&#xff1a;3.0s Python时间限制&#xff1a;5.0s &#xff08;2&#xff09;输入 输入一个正整数N &#xff08;3&#xff09;输出 N有几个约数 &a…...

魔法反射--java反射初入门(基础篇)

&#x1f473;我亲爱的各位大佬们好&#x1f618;&#x1f618;&#x1f618; ♨️本篇文章记录的为 java反射初入门 相关内容&#xff0c;适合在学Java的小白,帮助新手快速上手,也适合复习中&#xff0c;面试中的大佬&#x1f649;&#x1f649;&#x1f649;。 ♨️如果文章有…...

概率统计_协方差的传播 Covariance Propagation

1. 方差的传播 误差的传播是指分析在形如的关系中,参量误差(x)对变量误差(y)的影响有多大。误差的传播与函数的微分紧密相关,本质是在利用当Δ x 不大时,。 方差计算公式: X为变量,为总体均值,N为总体例数。求变量X与均值的差的平方再求平均值,即得到方差。方差…...

大学生考研的意义?

当我拿起笔头&#xff0c;准备写这个话题时&#xff0c;心里是非常难受的&#xff0c;因为看到太多的学生在最好的年华&#xff0c;在自由的大学本应该开拓知识&#xff0c;提升认知&#xff0c;动手实践&#xff0c;不断尝试和试错&#xff0c;不断历练自己跳出学生思维圈&…...

在软件开发中正确使用MySQL日期时间类型的深度解析

在日常软件开发场景中&#xff0c;时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志&#xff0c;到供应链系统的物流节点时间戳&#xff0c;时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库&#xff0c;其日期时间类型的…...

【项目实战】通过多模态+LangGraph实现PPT生成助手

PPT自动生成系统 基于LangGraph的PPT自动生成系统&#xff0c;可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析&#xff1a;自动解析Markdown文档结构PPT模板分析&#xff1a;分析PPT模板的布局和风格智能布局决策&#xff1a;匹配内容与合适的PPT布局自动…...

Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!

一、引言 在数据驱动的背景下&#xff0c;知识图谱凭借其高效的信息组织能力&#xff0c;正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合&#xff0c;探讨知识图谱开发的实现细节&#xff0c;帮助读者掌握该技术栈在实际项目中的落地方法。 …...

算法笔记2

1.字符串拼接最好用StringBuilder&#xff0c;不用String 2.创建List<>类型的数组并创建内存 List arr[] new ArrayList[26]; Arrays.setAll(arr, i -> new ArrayList<>()); 3.去掉首尾空格...

GruntJS-前端自动化任务运行器从入门到实战

Grunt 完全指南&#xff1a;从入门到实战 一、Grunt 是什么&#xff1f; Grunt是一个基于 Node.js 的前端自动化任务运行器&#xff0c;主要用于自动化执行项目开发中重复性高的任务&#xff0c;例如文件压缩、代码编译、语法检查、单元测试、文件合并等。通过配置简洁的任务…...

MySQL 知识小结(一)

一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库&#xff0c;分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷&#xff0c;但是文件存放起来数据比较冗余&#xff0c;用二进制能够更好管理咱们M…...

Razor编程中@Html的方法使用大全

文章目录 1. 基础HTML辅助方法1.1 Html.ActionLink()1.2 Html.RouteLink()1.3 Html.Display() / Html.DisplayFor()1.4 Html.Editor() / Html.EditorFor()1.5 Html.Label() / Html.LabelFor()1.6 Html.TextBox() / Html.TextBoxFor() 2. 表单相关辅助方法2.1 Html.BeginForm() …...

Unity UGUI Button事件流程

场景结构 测试代码 public class TestBtn : MonoBehaviour {void Start(){var btn GetComponent<Button>();btn.onClick.AddListener(OnClick);}private void OnClick(){Debug.Log("666");}}当添加事件时 // 实例化一个ButtonClickedEvent的事件 [Formerl…...

LangFlow技术架构分析

&#x1f527; LangFlow 的可视化技术栈 前端节点编辑器 底层框架&#xff1a;基于 &#xff08;一个现代化的 React 节点绘图库&#xff09; 功能&#xff1a; 拖拽式构建 LangGraph 状态机 实时连线定义节点依赖关系 可视化调试循环和分支逻辑 与 LangGraph 的深…...

HybridVLA——让单一LLM同时具备扩散和自回归动作预测能力:训练时既扩散也回归,但推理时则扩散

前言 如上一篇文章《dexcap升级版之DexWild》中的前言部分所说&#xff0c;在叠衣服的过程中&#xff0c;我会带着团队对比各种模型、方法、策略&#xff0c;毕竟针对各个场景始终寻找更优的解决方案&#xff0c;是我个人和我司「七月在线」的职责之一 且个人认为&#xff0c…...