【C语言进阶(11)】动态内存管理

文章目录

  • Ⅰ 存在动态内存分配的原因
  • Ⅱ 动态内存函数
    • 1. malloc
    • 2. calloc
    • 3. realloc
    • 4. free (重要)
  • Ⅲ 常见动态内存错误
    • 1. 对 NULL 指针的解引用操作
    • 2. 对动态开辟空间的越界访问
    • 3. 对非动态开辟内存使用 free 释放
    • 4. 使用 free 释放一块动态开辟内存的一部分
    • 5. 对同一块动态内存多次释放
    • 6. 动态开辟内存没有释放(内存泄漏)
  • Ⅳ 柔性数组

引用头文件

stdlib.h

Ⅰ 存在动态内存分配的原因

内存使用的方式

  1. 创建变量(开辟一块独立的空间)

    • 局部变量 -> 栈区。
    • 全局变量 -> 静态区。
  2. 创建数组(开辟一块连续的 空间)

    • 局部数组 -> 栈区。
    • 全局数组 -> 静态区。

上述开辟空间的方式有两个特点

  1. 空间开辟的大小是固定的
  2. 数组在定义的时候,必须指定数组的长度,它所需要的内存在编译时分配。

存在的问题

  • 不能灵活的开辟空间。如果需要的空间很少,但是固定开辟的空间非常大,就会造成内存浪费。如果需要的空间很多,固定开辟的空间很小,就会造成内存不够用的情况。
  • 因此就出现了动态内存函数,用来随时更改所开辟的空间的大小。

Ⅱ 动态内存函数

1. malloc

函数原型

void* malloc (size_t size);

函数功能

  • 开辟一块 size 个字节大小连续可用的空间,并返回该空间的起始地址。

函数返回值

  • 如果开辟成功,则返回一个指向开辟好空间的起始地址的指针。
  • 如果开辟失败,则返回一个 NULL 指针。因此要检查好 malloc 的返回值。
  • 返回值的类型是 void*,在使用指向开辟好的空间的起始地址的指针时,还需要转换成自己需要的指针类型。

函数用例

  • 申请一块空间,用来存放 10 个整形。
#include <stdlib.h>int main()
{int* p = (int*)malloc(10 * sizeof(int));//malloc 返回的是 void* 的指针,需要转换成对应的指针类型才能使用return 0;
}

2. calloc

函数原型

void* calloc (size_t num, size_t size);

函数功能

  • 为 num 个大小为 size 的元素开辟一块空间,并且把该空间的每个字节初始化为 0。
  • 与 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全0。

函数用例

  • 开辟一块具有 10 个元素,每个元素 4 个字节的空间。

在这里插入图片描述

3. realloc

函数原型

void* realloc (void* ptr, size_t size);

函数功能

  • 调整由 ptr 所指向的空间大小为 size 个字节。

函数参数

  • ptr:指向由 malloc、calloc 所开辟的空间。
  • size:新空间的大小(字节)。

realloc 函数的两种使用情况

void* realloc (void* ptr, size_t size);
  1. 将 ptr 所指向的空间大小重新调整为 size 个字节。
void* realloc(NULL,size_t size);
  1. 开辟一块有 size 个字节的空间。

函数用例

在这里插入图片描述

realloc 调整空间的方式

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

  1. 情况 1:原有空间之后足够大的空间,扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发生变化,返回旧地址

在这里插入图片描述

  1. 情况 2:原有空间之后没有足够大的空间,realloc 会重新找一个内存区域开辟一块符合要求的空间,返回新地址,拷贝旧数据,释放旧空间

在这里插入图片描述

4. free (重要)

函数原型

void free (void* ptr);

函数参数

  • ptr:指向先前用 malloc、calloc、realloc 分配的内存块的起始地址。

函数功能

  • 释放由 ptr 指向的动态开辟的空间。

函数用例

  • 释放由各个动态内存函数所开辟的空间。
int main()
{int* p1 = (int*)malloc(10 * sizeof(int));int* p2 = (int*)calloc(10, sizeof(int));int* p3 = (int*)realloc(NULL, sizeof(int) * 10);//......free(p1);free(p2);free(p3);return 0;
}

注意事项

  • free 会将指针指向的空间释放掉,但是不会改变指针内存放的地址值。
  • 也就是说,即使 free 掉了 p1 p2 p3 三个指针所指向的空间,这三个指针依然记得原先空间的起始地址。
  • 因此释放掉指针所指向的空间之后,还必须要将该指针变量的值置为 NULL。
int main()
{int* ptr = (int*)malloc(10 * sizeof(int));//......free(ptr);	//释放掉ptr指向的空间后,ptr内存的地址值未变,还是记得原来空间的地址ptr = NULL;	//因此要主动修改ptr内存放的地址值为NULLreturn 0;
}

Ⅲ 常见动态内存错误

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

  • 在使用之前未判断动态内存函数的返回值是否是 NULL,如果内存开辟失败返回的是 NULL,就成了对 NULL 指针的解引用操作。
int* p = (int*)malloc(40);
*p = 20;//使用前为判断指针 p 内的值是否有效

正确做法

int* p = (int*)malloc(40);if (NULL == p)	//使用之前先判断该空间是否开辟成功
{return 1;	//1 为异常返回
}*p = 20;

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

  • 动态申请的空间也有着自己的范围,不能无限制的使用。
int* p = calloc(10, sizeof(int));if (NULL == p)
{perror("calloc");return 1;
}for (int i = 0; i <= 10; i++)//当 i 到 10 时越界访问
{printf("%d ", *(p + i));
}
free(p);
p = NULL;

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

  • free 只能释放由 malloc、calloc、realloc 开辟的空间。

在这里插入图片描述

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

  • 指向开辟空间起始地址的指针产生变动,没有从动态开辟内存的起始地址开始释放空间。

在这里插入图片描述

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

  • 忘记了已经释放过动态开辟的空间,又重新释放了一遍。
int* p = (int*)malloc(10 * sizeof(int));
//......
free(p);//此时已经释放过该空间一次
//.....
free(p);//睡蒙了又释放一次

解决办法

  • 在释放完动态开辟好的内存后,及时将该指针置为 NULL。之后如果再重复释放也不会产生问题。

6. 动态开辟内存没有释放(内存泄漏)

  • 只记得申请空间,不记得还。
void test()
{int* p = (int*)malloc(10 * sizeof(int));//p 是局部变量,出了作用域就销毁,等这个函数一结束就没人再记得这块空间的起始地址了if (true)//此时动态开辟的空间永远没机会释放了{return;}free(p);p = NULL;
}int main()
{test();return 0;
}

Ⅳ 柔性数组

  • C99 中,结构中的最后一个元素允许是未知大小数组,这就叫做『柔性数组』成员。
struct S
{char c;int i;int arr[];	//未知大小数组 - 柔性数组成员
};

柔性数组的特点

  • 结构体中的柔性数组成员前面必须至少有一个其他成员。
  • sizeof 返回的这种结构体大小不包含柔性数组成员的大小。
  • 包含柔性数组成员的结构体用 malloc 函数进行内存的动态分配,并且分配的内存应该大于结构体的大小,以适应柔性数组的预期大小。
struct S
{char c;int i;int arr[];
};int main()
{struct S* ps = (struct S*)malloc(sizeof(struct S) + 20);//这 20 个字节才是分配给柔性数组 arr 的return 0;
}

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

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

相关文章

《深度不确定条件下的决策:从理论到实践》PDF

制定未来计划时需要预测变化&#xff0c;尤其是制定长期计划或针对罕见事件的计划时。当这些变化存在高度不确定性的时候&#xff0c;这种预期就变得越来越困难。 今天给大家介绍的这本《深度不确定条件下的决策&#xff1a;从理论到实践》正是解决以上问题的良方。完整书籍文…

深入理解JVM虚拟机第十一篇:详细介绍JVM中运行时数据区

文章目录 前言 一:运行时数据区详解 1:线程私有和线程公有区域 2:阿里的运行时数据区图

数据结构与算法-顺序表

数据结构与算法 &#x1f388;1.线性表&#x1f50e;1.1基本操作&#x1f50e;1.2线性表的存储结构 &#x1f388;2.线性表的顺序表示和实现&#x1f50e;2.1线性表的顺序存储表示&#x1f52d;2.1.1静态顺序表&#x1f52d;2.1.2动态顺序表 &#x1f50e;2.2顺序表基本操作的实…

设计有效的异常测试用例:关注这些方向,保障软件稳定性

在软件测试过程中&#xff0c;我们通常会关注基本功能测试、性能测试、压力测试等常规场景。然而&#xff0c;随着软件使用时间的增长&#xff0c;一些突发情况或者异常场景也可能会出现。为了保障软件的稳定性和质量&#xff0c;我们需要在测试中加入异常测试的环节。本文将围…

PL/SQL动态SQL

目录 1. 动态 sql 2. 带参数的动态 sql -- 不使用 USING 传参 1. 动态 sql -- 在 PL/SQL 程序开发中,可以使用 DML 语句,但是很多语句(如 DDL),不能直接在 PL/SQL中执行,这些语句可以使用动态 sql 来实现. 语法格式: EXECUTE IMMEDIATE --动态语句的字符串 [into 变量…

安卓:解决AndroidStudio导出Unity的Apk(APP)出现2个显示图标

用AndroidStudio打开该项目 实现只保留1个app图标 AndroidManifest.xml的改法如下&#xff1a; <?xml version"1.0" encoding"utf-8"?> <manifest xmlns:android"http://schemas.android.com/apk/res/android" package"com.fru…

【开发篇】十四、SpringBoot整合Quartz实现定时任务

文章目录 1、关于定时任务2、Java原生实现3、相关名词4、SpringBoot整合Quartz5、Quartz的通用配置6、关于QuartzJobBean7、关于调度器Scheduler的绑定8、Quartz持久化 1、关于定时任务 定时任务在实际开发中使用场景很多&#xff0c;比如&#xff1a; 年度报告各种统计报告某…

使用 gst-plugins-bad 里面的 gst-element-maker 工具创建gstreamer 插件

系列文章目录 创建 gstreamer 插件的几种方式 使用 gst-template 创建自己的 gstreamer 插件 使用 gst-plugins-bad 里面的 gst-element-maker 工具创建gstreamer 插件 使用 gst-element-maker 创建一个完全透传的 videofilter 插件 文章目录 系列文章目录前言一、获取gst-plu…

【小程序 - 加强】自定义组件、使用npm包、全局数据共享、分包_05

目录 一、自定义组件 1. 组件的创建与引用 1.1 创建组件 1.2 引用组件 1.2.1 局部引用组件 1.2.2 全局引用组件 1.2.3 全局引用 VS 局部引用 1.2.4 组件和页面的区别 2. 样式 2.1 组件样式隔离 2.2 组件样式隔离的注意点 2.3 修改组件的样式隔离选项 2.4 styleIso…

洛谷 P1148 拱猪计分

【题目链接】 洛谷 P1148 拱猪计分 题目补充说明&#xff1a; D10与D11都可能出现&#xff0c;D10作用与D11相同。如果D10与D11同时出现&#xff0c;只算有1张D牌。 当H1~H13都有&#xff0c;但D或S只有1个时&#xff1a;D牌作用&#xff1a;-100分&#xff0c;S牌作用&#x…

vue 基于vue-seamless-scroll无缝滚动的用法和遇到的问题解决

vue 基于vue-seamless-scroll无缝滚动的用法和遇到的问题解决 背景 最近再做一个大屏项目,需要用到表格滚动效果,之前自己写过js实现,最近发现一个组件vue-seamless-scroll可以实现滚动,感觉挺方便的,准备用一下,但是用完之后才发现这个组件有很多坑需要解决.我把用法和一些问…

C++(List)

本节目标&#xff1a; 1.list介绍及使用 2.list深度剖析及模拟实现 3.list和vector对比 1.list介绍及使用 1.1list介绍 1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器&#xff0c;并且该容器可以前后双向迭代。 2. list的底层是双向链表结构&#xff0c;…

内网综合扫描工具-fscan的安装和使用

简介 一款内网综合扫描工具&#xff0c;方便一键自动化、全方位漏扫扫描。 支持主机存活探测、端口扫描、常见服务的爆破、ms17010、redis批量写公钥、计划任务反弹shell、读取win网卡信息、web指纹识别、web漏洞扫描、netbios探测、域控识别等功能。相当强大&#xff01;&…

java复习-多态性

多态性 在Java中对于多态性由两种实现的模式&#xff1a; 方法的多态性 方法的重载&#xff1a;同一个方法名称可以根据传入的参数类型和个数的不同&#xff0c;进行不同的处理。 方法的覆写&#xff1a;同一个方法可能根据使用子类的不同&#xff0c;由不同的实现。 对象的…

国庆10.04

服务器 代码 头文件 #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include<QTcpServer> //服务器头文件 #include<QTcpSocket> //客户端头文件 #include<QList> //链表容器 #include<QMessag…

OpenCV(Python)的二值化示例

# -*-coding:utf-8-*- # src&#xff1a; 输入图&#xff0c;只能输入单通道图像&#xff0c;通常来说为灰度图 # - dst&#xff1a; 输出图 # - thresh&#xff1a; 阈值 # - maxval&#xff1a; 当像素值超过了阈值&#xff08;或者小于阈值&#xff0c;根据type来决定&#…

ALSA project the C library reference (ALSA工程 C库参考说明)

作者: Jaroslav Kysela perexperex.cz Abramo Bagnara abramoalsa-project.org Takashi Iwai tiwaisuse.de Frank van de Pol fvdpolcoil.demon.nl前言: 高级linux音频架构(ALSA)来自内核API和库的API.这个篇文章描述了应用层库API和内核层API对应是怎么的interfaces.API用法: …

CAA开发,3DE服务mql、lic 、cnext等部分参考指令

文章目录 1.在登陆3DE平台&#xff0c;没有许可证需要移除2.查询lic命令3.在3DE平台&#xff0c;显示机器没有安装此应用需要重新分配lic4.其他无关指令参考&#xff08;按相关信息进入cnext&#xff09;5.CAA项目快捷方式属性目标参考6.CAA的参考VB启动脚本 1.在登陆3DE平台&a…

【重拾C语言】二、顺序程序设计(基本符号、数据、语句、表达式、顺序控制结构、数据类型、输入/输出操作)

目录 前言 二、顺序程序设计 2.1 求绿化带面积——简单程序 2.2基本符号&#xff1a; 2.2.1 字符集 可视字符 不可视字符 2.2.2 C特定符 关键字 分隔符 运算符 2.2.3 标识符 2.2.4 间隔符 2.2.5 注释 2.3 数据 2.3.1 字面常量&#xff08;Literal Constants&am…

扫雷小游戏(简单详细)(内附完整代码)

设计总体思路 实现游戏可以一直玩&#xff0c;先打印棋盘&#xff0c;玩家和电脑下棋&#xff0c;最后分出胜负。 如果编写较大的程序&#xff0c;我们可以分不同模块 例如这个扫雷&#xff0c;我们可以创建三个文件 分别为&#xff1a; game.h 函数的声明game.c 函数的…