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

动态内存管理

目录

  • 1.为什么要动态内存分配
  • 2.动态内存函数
    • malloc](https://cplusplus.com/reference/cstdlib/malloc/?kw=malloc)和[free
    • calloc
    • realloc
  • 3.使用动态内存要注意的几点
    • 对NULL的解引用
    • 对同一块动态内存多次释放
    • free非动态开辟的内存
    • 使用free释放一块动态开辟内存的一部分
    • 一个函数中开辟动态空间传到其他函数中,同样需要free
  • 4.例题
    • 例题1
    • 例题2

1.为什么要动态内存分配

计算机的内存,粗略得可以分为栈区、堆区、静态区
在这里插入图片描述

我们之前学到的内存开辟是定义一个变量或定义一个数组

int num = 10;
int arr[10] = {0};

上述都是在栈区上开辟的空间,这样开辟的空间有两个特点:
1.空间开辟大小是固定的
2.数组在定义时,在VS环境中C99的规定下,必须指定数组长度,并且数组长度必须是常量,不可以是变量

对于空间的需求,如果我们知道要开辟空间的大小,那么可以用上述的开辟方式
但是有时候我们需要的空间大小在程序运行的时候才能知道,那数组的编译时开辟空间的方式就不能满足了,这就需要用动态内存开辟了


2.动态内存函数

C语言中,有一些动态开辟的库函数,这些函数都声明在stdlib.h头文件中
并且这些函数是在内存中的堆区开辟的空间

malloc和free

void* malloc (size_t size);
  • 这个函数向内存中申请一块连续可用的空间,并且返回指向这块空间的指针
  • 如果开辟失败,返回一个NULL指针,所以在malloc后要检查返回值
  • 因为函数不知道开辟的空间是什么类型,所以在函数设计时,就设计返回一个void*指针
  • 返回值类型是void*,在使用时由使用者决定,所以要把返回的指针进行强制类型转化成其他类型的指针
  • 如果参数size为0,malloc的行为是C语言中未规定的,取决于编译器

下面我们开辟一个存放int类型的空间

#include <stdio.h>
#include <stdlib.h>
#include<string.h>
#include<errno.h>int main()
{int* arr = NULL;arr = (int*)malloc(40);//检测是否malloc成功if (arr == NULL){strerror(errno);return 1;}
}

开辟出的是连续的空间,所以与数组类似,我们可以通过下标访问空间

#include <stdio.h>
#include <stdlib.h>
#include<string.h>
#include<errno.h>int main()
{int* arr = NULL;arr = (int*)malloc(40);//检测是否malloc成功if (arr == NULL){strerror(errno);return 1;}int i = 0;for(i=0; i<num; i++){*(arr+i) = 0;//arr[i] = 0;通过下标访问}
}

C语言中提供了另一个free函数,专门是用来动态内存的释放和回收

void free (void* ptr);
  • free函数是专门用来释放动态开辟的内存,ptr指向动态开辟的空间。
  • 如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的。
  • 如果参数 ptr是NULL指针,则函数什么事都不做。
  • free掉空间后还要把ptr置为空

int main()
{int* arr = NULL;arr = (int*)malloc(40);//检测是否malloc成功if (arr == NULL){strerror(errno);return 1;}int i = 0;for(i=0; i<num; i++){*(arr+i) = 0;//arr[i] = 0;通过下标访问}free(arr);//释放ptr所指向的动态内存arr = NULL;return 0;
}

calloc

函数原型:

void* calloc (size_t num, size_t size);
  • 该函数的功能是开辟num个大小为size的元素开辟一块空间
  • 该数会把开辟出的空间每个字节初始化为0
  • 其他与malloc用法相同
int main()
{int *p = (int*)calloc(10,sizeof(int));//检测是否calloc成功if (arr == NULL){strerror(errno);return 1;}int i = 0;for(i=0; i<num; i++){*(arr+i) = 0;//arr[i] = 0;通过下标访问}free(arr);//释放ptr所指向的动态内存arr = NULL;return 0;
}

realloc

realloc函数使动态内存管理更加灵活

有时我们发现之前申请的动态空间太小了,或者太大了,为了得到合理大小的内存,我们就要用到realloc函数对内存大小进行

函数原型:

void* realloc (void* ptr, size_t size);
  • ptr是要调整的内存地址,size是调整后的大小
  • 返回值是调整之后的内存起始位置
  • 这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间。
  • realloc开辟出的新空间不会初始化
  • 要用一个新的指针去接收realloc调整后的地址,因为如果用旧的指针去维护它,如果扩容失败,返回NULL,不但扩容失败了,原空间中的数据也丢失了

realloc调整内存空间有2种情况:

情况一:原有空间后有足够大的空间进行扩容

要扩展内存就直接在原有内存之后直接追加空间,原来空间的数据不发生变化
在这里插入图片描述

情况2:原有空间之后没有足够大的空间进行扩容

在堆空间上另找一个合适大小的连续空间来使用。这样函数返回的是一个新的内存地址。
原内存空间中的内容也会拷贝到新的空间上
在这里插入图片描述

int main()
{int* p = (int*)malloc(5 * sizeof(int));if (p == NULL){perror("malloc");return 1;} int* ptr = (int*)realloc(p, 10 * sizeof(int));if (ptr != NULL){p = ptr;//realloc成功,就把新地址的值赋给旧地址,还是让旧指针维护这个空间}//realloc在开辟空间后,不会进行初始化free(p);p = NULL;return 0;
}

3.使用动态内存要注意的几点

对NULL的解引用

void test()
{int *p = (int *)malloc(INT_MAX/4);*p = 20;//如果p的值是NULL,就会有问题free(p);
}

对同一块动态内存多次释放

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

free非动态开辟的内存

void test()
{int a = 10;int *p = &a;free(p);//不可以free掉动态开辟的内存
}

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

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

一个函数中开辟动态空间传到其他函数中,同样需要free

#include <stdio.h>
#include <stdlib.h>int* getmem()
{int* p = (int*)malloc(40);return p;
}
int main()
{int* arr = getmem();free(arr);//需要free掉通过函数传来的动态内存arr = NULL;return 0;
}

4.例题

例题1

以下代码有什么错误?

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

*1. str传给ppstr的临时拷贝,有自己独立的空间,在p中进行动态内存开辟,但是str仍为空,strcpy拷贝时,非法访问内存
* 在这里插入图片描述

  • 2.在GetMemory中开辟了动态内存,但是并没有free释放掉,所以会内存泄漏

想要解决这个问题,改变指针的指向,就要用到二级指针

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

例题2

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

上面的代码也有问题,GetMemory函数中定义了一个字符串,并且返回字符串首字符地址,主函数中用str接收,但是出了GetMemory后,在GetMemory中定义的局部变量会自动销毁,所以str还是一个空指针

像这种问题都叫做返回栈空间地址的问题

相关文章:

动态内存管理

目录1.为什么要动态内存分配2.动态内存函数malloc](https://cplusplus.com/reference/cstdlib/malloc/?kwmalloc)和[freecallocrealloc3.使用动态内存要注意的几点对NULL的解引用对同一块动态内存多次释放free非动态开辟的内存使用free释放一块动态开辟内存的一部分一个函数中…...

Unsupervised Question Answering 简单综述

Unsupervised Question Answering by Cloze Translation, ACL 2019 随机从文本中抽取noun phrases或者named entity作为答案将答案部分mask掉&#xff0c;生成cloze question利用无监督翻译&#xff0c;将cloze question转化为natural question 缺点&#xff1a; 直接利用原句…...

智慧物流管理系统

智慧物流运用物联网、大数据、云计算、人工智能等技术优化物流决策过程。智慧物流获取、分析物流信息并做出决策&#xff0c;从商品源开始实时跟踪与管理&#xff0c;保证信息流快于商品流&#xff0c;实现信息与物质快速、高效、流畅地运转&#xff0c;集自动化、数字化、网络…...

单表查询--实例

#素材&#xff1a; 表名&#xff1a;worker-- 表中字段均为中文&#xff0c;比如 部门号 工资 职工号 参加工作 等 >CREATE TABLE worker ( >部门号 int(11) NOT NULL, >职工号 int(11) NOT NULL, >工作时间 date NOT NULL, >工资 float(8,2) NOT NULL, >政治…...

c语言递归 累和 ,累乘积,斐波那契数列,字符串长度

目录 递归使用场景 1:使用递归的方式计算 Sn123..100 2&#xff1a;计算 n&#xff01;n*(n-1)*(n-2)*......*1; 3:计算输出斐波那契数列前20项&#xff0c;并按每行4个数的格式输出(2019年&#xff09; 4&#xff1a; 用递归和非递归两种方式编写函数strlength()。该函数…...

数据与C(ASCII码,char)

目录 一.ASCII码讲解 二.非打印字符&#xff08;转义字符&#xff09; 三.扩展小知识 一.ASCII码讲解 char类型用于存储字符&#xff0c;从技术层面看&#xff0c;char时整数类型&#xff0c;因为char类型实际上存储的是整数而不是字符。计算机使用数字编码来处理字符&…...

第一个C语言代码(visual studin创建调试以及项目文件功能讲解)

这里我主要使用visual Studio进行编程 目录 一.创建项目 二.编写代码 1.代码编写 2.代码分析 3.main() 4.注释符 5.{} 花括号 6.声明 7.赋值 8.printf()函数 9.return 0; 一.创建项目 这里大家可能会比较疑惑&#xff0c;为啥都是C&#xff0c;没看见C的项目&…...

VIF原理

文章目录一、VIF公式和原理对于R方一般回归模型皮尔逊相关系数中的方差VIF原理&#xff1a;一、VIF公式和原理 所谓VIF方法&#xff0c;计算难度并不高。在线性回归方法里&#xff0c;应用最广泛的就是最小二乘法&#xff08;OLS&#xff09;&#xff0c;只不过我们对每个因子…...

nginx相关反爬策略总结笔记

引言 互联网站点的流量一部分由人类正常访问行为产生&#xff0c;而高达30%-60%的流量则是由网络爬虫产生的&#xff0c;其中一部分包含友好网络爬虫&#xff0c;如搜索引擎的爬虫、广告程序、第三方合作伙伴程序、Robots协议友好程序等;而并非所有的网络爬虫都是友好的&#x…...

【Vue3】电商网站吸顶功能

头部分类导航-吸顶功能 电商网站的首页内容会比较多&#xff0c;页面比较长&#xff0c;为了能让用户在滚动浏览内容的过程中都能够快速的切换到其它分类。需要分类导航一直可见&#xff0c;所以需要一个吸顶导航的效果。 目标:完成头部组件吸顶效果的实现 交互要求 滚动距离大…...

HOMER docker版本安装详细流程

概述 HOMER是一款100%开源的针对SIP/VOIP/RTC的抓包工具和监控工具。 HOMER是一款强大的、运营商级、可扩展的数据包和事件捕获系统&#xff0c;是基于HEP/EEP协议的VoIP/RTC监控应用程序&#xff0c;并可以使用即时搜索、处理和存储大量的信令、RTC事件、日志和统计信息。 …...

【数据结构】单向链表的练习题

目录 前言 1、删除链表中等于给定值val的所有节点。 【题目描述】 【代码示例】 【 画图理解】 2、反转一个点链表 【题目描述】 【 代码思路】 【代码示例】 【画图理解】 3、给定一个带有头节点head的非空单链表&#xff0c;返回链表的中间节点&#xff0c;如果有两个…...

我的企业需要一个网站吗?答案是肯定的 10 个理由

如果您的企业在没有网站的情况下走到了这一步&#xff0c;您可能会想&#xff1a;我的企业需要一个网站吗&#xff1f;如果我的企业没有一个就已经成功了&#xff0c;那又有什么意义呢&#xff1f;简短的回答是&#xff0c;现在是为您的企业投资网站的最佳或更重要的时机。网站…...

CHI协议定义的NOC组件

请求结点RN 可以向NOC发送读/写等请求事务&#xff0c;有以下几种类型的RN&#xff1a; RN-F 一般是处理器核或者核簇结点&#xff0c;包含了局部cache和一致性部件snoopee。与NOC上的一致性部件一起&#xff0c;维护“可缓存”数据的一致性&#xff08;这种可缓存数据…...

Python+Flask+MySQL开发的在线外卖订餐系统(附源码)

文章目录一、项目模块及功能介绍1、登录模块2、注册模块3、商家用户模块4、买家用户模块5、系统管理员模块源码二、项目结构三、环境依赖四、运行方法五、系统部分界面展示1、首页2、注册界面3、登录界面4、商家主界面5、商家菜单界面6、商家添加菜品界面7、商家修改菜品界面8、…...

OpenStack云平台搭建(4) | 部署Placement

目录 安装部署Placement 1、登录数据库授权 2、安装palcement-api 安装部署Placement 【Placement】服务 是从【nova】服务中拆分出来的组件&#xff0c;作用是收集各个【node】节点的可用资源&#xff0c;把【node】节点的资源统计写入到【MySQL】【Placement】服务会被【n…...

GNN图神经网络原理解析

一、GNN基本概念 1. 图的基本组成 图神经网络的核心就是进行图模型搭建,图是由点和边组成的。在计算机处理时,通常将数据以向量的形式进行存储。因此,在存储图时,就会有点的向量,点与点之间边的向量,全局向量(描述整张图),邻接矩阵(记录哪些点之间存在关联)等。 既…...

BI-SQL丨ALL、ANY、SOME

ALL、ANY、SOME ALL、ANY和SOME&#xff0c;这三个关键字&#xff0c;在SQL中使用频率较高&#xff0c;通常可以用来进行数据比较筛选。 注&#xff1a;SQL中ALL的用法和DAX中ALL的用法是完全不同的&#xff0c;小伙伴不要混淆了。 那么三者之间的区别是什么呢&#xff1f; A…...

从0到0.1学习 maven(三:声明周期、插件、聚合与继承)

该文章为maven系列学习的第三篇&#xff0c;也是最后一篇 第一篇快速入口&#xff1a;从0到0.1学习 maven(一&#xff1a;概述及简单入门) 第二篇快速入口&#xff1a;从0到0.1学习 maven(二&#xff1a;坐标、依赖和仓库) 文章目录啥子叫生命周期生命周期详解clean生命周期def…...

【直击招聘C++】2.5 this指针

2.5 this指针一、要点归纳1.什么是this指针2.this指针的深入讨论程序1程序23.类成员函数返回对象和返回对象引用的区别二、面试真题解析面试题1面试题2一、要点归纳 1.什么是this指针 this指针是隐含于每一个类对象的特殊指针&#xff0c;该指针值是一个正在被某个成员函数操作…...

[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?

&#x1f9e0; 智能合约中的数据是如何在区块链中保持一致的&#xff1f; 为什么所有区块链节点都能得出相同结果&#xff1f;合约调用这么复杂&#xff0c;状态真能保持一致吗&#xff1f;本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里&#xf…...

WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成

厌倦手动写WordPress文章&#xff1f;AI自动生成&#xff0c;效率提升10倍&#xff01; 支持多语言、自动配图、定时发布&#xff0c;让内容创作更轻松&#xff01; AI内容生成 → 不想每天写文章&#xff1f;AI一键生成高质量内容&#xff01;多语言支持 → 跨境电商必备&am…...

高防服务器能够抵御哪些网络攻击呢?

高防服务器作为一种有着高度防御能力的服务器&#xff0c;可以帮助网站应对分布式拒绝服务攻击&#xff0c;有效识别和清理一些恶意的网络流量&#xff0c;为用户提供安全且稳定的网络环境&#xff0c;那么&#xff0c;高防服务器一般都可以抵御哪些网络攻击呢&#xff1f;下面…...

【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分

一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计&#xff0c;提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合&#xff1a;各模块职责清晰&#xff0c;便于独立开发…...

Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理

引言 Bitmap&#xff08;位图&#xff09;是Android应用内存占用的“头号杀手”。一张1080P&#xff08;1920x1080&#xff09;的图片以ARGB_8888格式加载时&#xff0c;内存占用高达8MB&#xff08;192010804字节&#xff09;。据统计&#xff0c;超过60%的应用OOM崩溃与Bitm…...

VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP

编辑-虚拟网络编辑器-更改设置 选择桥接模式&#xff0c;然后找到相应的网卡&#xff08;可以查看自己本机的网络连接&#xff09; windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置&#xff0c;选择刚才配置的桥接模式 静态ip设置&#xff1a; 我用的ubuntu24桌…...

【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制

使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下&#xff0c;限制某个 IP 的访问频率是非常重要的&#xff0c;可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案&#xff0c;使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...

免费数学几何作图web平台

光锐软件免费数学工具&#xff0c;maths,数学制图&#xff0c;数学作图&#xff0c;几何作图&#xff0c;几何&#xff0c;AR开发,AR教育,增强现实,软件公司,XR,MR,VR,虚拟仿真,虚拟现实,混合现实,教育科技产品,职业模拟培训,高保真VR场景,结构互动课件,元宇宙http://xaglare.c…...

Python竞赛环境搭建全攻略

Python环境搭建竞赛技术文章大纲 竞赛背景与意义 竞赛的目的与价值Python在竞赛中的应用场景环境搭建对竞赛效率的影响 竞赛环境需求分析 常见竞赛类型&#xff08;算法、数据分析、机器学习等&#xff09;不同竞赛对Python版本及库的要求硬件与操作系统的兼容性问题 Pyth…...

快速排序算法改进:随机快排-荷兰国旗划分详解

随机快速排序-荷兰国旗划分算法详解 一、基础知识回顾1.1 快速排序简介1.2 荷兰国旗问题 二、随机快排 - 荷兰国旗划分原理2.1 随机化枢轴选择2.2 荷兰国旗划分过程2.3 结合随机快排与荷兰国旗划分 三、代码实现3.1 Python实现3.2 Java实现3.3 C实现 四、性能分析4.1 时间复杂度…...