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

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

目录

1. 引言

2. malloc函数

2.1 malloc的使用

2.2 与正常开辟空间的区别

存储区域不同 

2.3 变长数组可变?

2.4 free是做什么的? 

 2.4.1 关于free的注意事项


        本章节内容来介绍c语言的动态内存管理,可能内容较多这里分为多个博文来分别描述,动态内存管理是C语言进阶部分中比较重要的内容,重要程度不亚于指针。

1. 引言

        在C语言中,申请内存空间一般就是一次申请,不能更改,例如下面就申请了固定的字节。

int main()
{int a = 10; // 4个字节int arr[10]; // 40个字节return 0;
}

        再例如之前写的结构体项目:通讯录使用的就是结构体数组,里面固定存放了100个结构体,这就会造成一个问题,如果我只有20个联系人就会浪费掉80个空间,如果我有120个联系人,空间就会不够;这种实现方式不够灵活,所以我们希望能够自己维护自己的内存空间。

        本质上需要理解四个函数:malloc、calloc、realloc、free。

2. malloc函数

        查看函数的描述:这个函数能够开辟一块内存空间,返回这块空间的起始指针(地址);这个函数的形参是字节数,返回值是void*,需要强转成自己需要的类型。

        在申请空间的时候,也要考虑到开辟失败的可能性,一旦开辟失败,返回的是空指针。、

2.1 malloc的使用

         当使用malloc函数的时候需要注意,这里是用作动态开辟int类型的数组,为了以后方便操作所以需要对返回值进行强制类型转换成自己所需要的类型。

        strerror的头文件是string.h;

        errno的头文件是errno.h;

        malloc的头文件是stdlib.h

        这里的errno是错误码,strerror可以将错误码转换成字符串信息,一旦空间分配失败,就打印错误信息,返回1表示程序非正常结束。

#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>int main()
{// 常规方法int arr[10] = { 0 };// 动态内存分配int* p = (int*)malloc(40);if (p == NULL) // 分配空间失败{printf("%s\n", strerror(errno));return 1;}// 使用刚刚的空间int i = 0;for (i = 0; i < 10; i++){*(p + i) = i;}for (i = 0; i < 10; i++){printf("%d\n", *(p + i));}return 0;
}

 此时运行没有报错,运行如下:

我们再展示一下开辟内存失败的情况,将内存开辟字节数换成INT_MAX大概是21亿左右:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>int main()
{// 常规方法int arr[10] = { 0 };// 动态内存分配int* p = (int*)malloc(INT_MAX);if (p == NULL) // 分配空间失败{printf("%s\n", strerror(errno));return 1;}// 使用刚刚的空间int i = 0;for (i = 0; i < 10; i++){*(p + i) = i;}for (i = 0; i < 10; i++){printf("%d\n", *(p + i));}return 0;
}

 运行结果就是将错误码打印的错误信息(32位运行环境):

2.2 与正常开辟空间的区别

存储区域不同 

        内存可简单分为栈区、堆区、静态区等,下图可以展示申请空间存储区域的不同:

在堆区申请的空间是可以随意调整大小,在栈区申请空间之后则不可以进行调整。

        如果有之前学过C语言的读者就会产生疑问,上面的代码申请了空间,但是没有使用free进行释放空间,这会导致内存泄漏吗?——其实当程序退出之后,系统会自动回收;

        什么是内存泄露呢?当申请一块内存空间,使用完毕之后需要“返还”,如果没有返还,相当于“丢了”,这就叫内存泄露。

2.3 变长数组可变?

        在C99中存在一个变长数组的内容,输入一个变量,我们可以动态地决定一个数组的长度类似于:

int n = 0;
scanf("%d",&n);
int arr[n];

        但是这并不意味着长度可变,还是申请一块固定的内存区域,所以取名有些迷惑。

2.4 free是做什么的? 

        在free处打一个断点进行调试,我们发现当执行到free语句的时候,此时p存放的地址仍然还是之前的地址,那么free做了什么呢?

        free 告诉编译器,这段空间我不用了,仅仅是做了一个声明而已;如果想要真正的释放空间,那么就需要手动置空,如下:

free(p);
p = NULL;

        如果只是free了这个指针会导致什么后果?这块地址还是能够被找到,编译器万一给了这块空间一些其他的数据,我们还是可以通过指针p来进行访问,这就非常的不安全,所以如何正确释放内容空间?

①free指针,告诉编译器,这块内存已经不在需要了,可以回收了。

②将指针置空,毁尸灭迹,不要让指针找到以前的地址。

        如果一直申请空间,从不释放,那么在程序运行过程中,内存的占用会不断增加,如下图所示:

        这段代码已经快占用内存2个GB的空间了, 如果以后在工作中遇到了一个项目一直在服务器中运行,过了一段时间就会崩溃,但是重启之后又好了,这就可以考虑是不是内存泄露的问题了。

 2.4.1 关于free的注意事项

①我们首先来看下面的代码:

int a = 10;
int *p = &a;
free(p);
p = NULL;

        这样写会报错,这里是开辟因为free只能释放动态开辟的空间(堆空间),而上面的变量a是在栈上开辟的,所以不能使用free释放。

②如果传给free一个空指针,这里什么都不会做。

int *p = NULL;
free(p); // do nothing

        本期内容到这就结束了,由于内容过多,下一期会介绍calloc等后续函数以及相关的用法和细节,如果你觉得本期内容不错的话,可以点赞收藏评论,支持一下,你的支撑是我更新的最大动力!!

http://www.xdnf.cn/news/1167931.html

相关文章:

  • 赋能未来数学课堂——基于Qwen3、LangChain与Agent架构的个性化教辅系统研究
  • Vue + WebSocket 实时数据可视化实战:多源融合与模拟数据双模式设计
  • vscode目录,右键菜单加入用VSCode打开文件和文件夹(快速解决)(含删除)(脚本)
  • 华为服务器操作系统openEuler介绍与安装
  • 信息学奥赛一本通 1553:【例 2】暗的连锁
  • C++_Hello算法_队列
  • Grails(Groovy)框架抛出NoHandlerFoundException而不是返回404 Not Found
  • Android-API调用学习总结
  • 专题 前端面试知识梳理大全
  • Leetcode题解:209长度最小的子数组,掌握滑动窗口从此开始!!!
  • Android13重置锁屏(1)
  • 碰一碰发视频源码搭建:支持OEM
  • 现在希望用git将本地文件crawler目录下的文件更新到远程仓库指定crawler目录下,命名相同的文件本地文件将其覆盖
  • 【Tomcat】Tomcat线程池深度调优手册(终极版)
  • 用USBi仿真器的SPI模式和IIC模式来调试DSP应该怎么做?
  • Vue项目中的AJAX请求与跨域问题解析
  • Linux CentOS 虚拟机升级内核至4.x以上版本
  • 异构融合 4A:重构高性能计算与复杂场景分析的安全与效率边界
  • Go 的第一类对象与闭包
  • Vercel AI SDK 3.0 学习入门指南
  • 厚铜板载流革命与精密压合工艺——高可靠性PCB批量制造的新锚点
  • 容器化部署 Tomcat + MySQL 实战指南:从入门到进阶
  • 分布式高可用ELK平台搭建及使用保姆级教程指南
  • 智能制造——解读52页汽车设计制造一体化整车产品生命周期PLM解决方案【附全文阅读】
  • linux用户态各定时器抖动测试
  • 操作符练习
  • 【Linux内核模块】模块声明与描述
  • nginx使用手册
  • 在easyui中如何自定义表格里面的内容
  • MCU中的总线桥是什么?