当前位置: 首页 > 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;不断历练自己跳出学生思维圈&…...

React hook之useRef

React useRef 详解 useRef 是 React 提供的一个 Hook&#xff0c;用于在函数组件中创建可变的引用对象。它在 React 开发中有多种重要用途&#xff0c;下面我将全面详细地介绍它的特性和用法。 基本概念 1. 创建 ref const refContainer useRef(initialValue);initialValu…...

django filter 统计数量 按属性去重

在Django中&#xff0c;如果你想要根据某个属性对查询集进行去重并统计数量&#xff0c;你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求&#xff1a; 方法1&#xff1a;使用annotate()和Count 假设你有一个模型Item&#xff0c;并且你想…...

PL0语法,分析器实现!

简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

AI,如何重构理解、匹配与决策?

AI 时代&#xff0c;我们如何理解消费&#xff1f; 作者&#xff5c;王彬 封面&#xff5c;Unplash 人们通过信息理解世界。 曾几何时&#xff0c;PC 与移动互联网重塑了人们的购物路径&#xff1a;信息变得唾手可得&#xff0c;商品决策变得高度依赖内容。 但 AI 时代的来…...

Python ROS2【机器人中间件框架】 简介

销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...

sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!

简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求&#xff0c;并检查收到的响应。它以以下模式之一…...

windows系统MySQL安装文档

概览&#xff1a;本文讨论了MySQL的安装、使用过程中涉及的解压、配置、初始化、注册服务、启动、修改密码、登录、退出以及卸载等相关内容&#xff0c;为学习者提供全面的操作指导。关键要点包括&#xff1a; 解压 &#xff1a;下载完成后解压压缩包&#xff0c;得到MySQL 8.…...

stm32wle5 lpuart DMA数据不接收

配置波特率9600时&#xff0c;需要使用外部低速晶振...

ubuntu系统文件误删(/lib/x86_64-linux-gnu/libc.so.6)修复方案 [成功解决]

报错信息&#xff1a;libc.so.6: cannot open shared object file: No such file or directory&#xff1a; #ls, ln, sudo...命令都不能用 error while loading shared libraries: libc.so.6: cannot open shared object file: No such file or directory重启后报错信息&…...