动态内存的定义及使用(附常见错误点)

简介: 动态内存的定义及使用(附常见错误点)

前言


平时我们在写代码的时候就常常要遇到这样的问题,有段空间的大小是在程序运行的时候才知道其具体的大小。而为了数据的完整往往需要开辟一块巨大的空间供程序使用。这时候,我们便想说有没有一种方法可以恰恰好开辟一个我们需要的空间,不会浪费内存的空间,也能保证程序的完整运行。于是,动态内存便孕育而生。他与我们平时创建的局部变量不同,局部变量是创建在内存的栈区之中,离开了其作用域,这个变量所占的空间就会被内存所回收,而动态内存则创建在内存的堆区,他的作用域就是整个程序,若无外界因素而导致这块动态内存被释放,则直到整个程序结束,这块开辟的内存空间则会一直存在。同时其向内存申请的大小可以根据需要随时调整。

image.png

malloc


事不宜迟,这就介绍动态内存的第一个函数。

image.png

通过查找资料我们得知, malloc 函数需要传参一个单位是字节、数据类型是size_t的数据,即申请一片大小为 size 字节的空间后由于系统并不知道这片空间是用来管理什么数据,所以他返回void*指针(方便使用者转换)。这是开辟成功的情况,若系统内存不足以开辟如此大的空间,则 malloc 函数便会返回一个空指针,正因如此对于 malloc 的返回值我们必须进行检查,若是对空指针进行解引用操作便会产生错误。同时 malloc 函数的头文件是 <stdlib.h> 所以得先引用其头文件之后才能正常使用这个函数。


如下所示,我们使用 malloc 申请了 40 个字节的空间并用转换成整型指针进行管理。不禁就想看看这个函数开辟的空间究竟是怎么样的。

image.png

进入调试后,找到 ptr 所指向的空间,我们便可以看到向内存申请的 40 个字节的空间,这时候我们便注意到这些空间里面都存放着 cd malloc 不会对申请的这片空间进行初始化,若要初始化这篇空间,只需要使用memset或是用循环遍历一遍就能完成。

image.png

image.png

calloc


这个函数跟 malloc 相似一样是在内存之中开辟动态内存,与之不同的是 calloc 所要传的参数比 malloc 多一个,即开辟 num size 大小的空间供程序员使用,同时,若开辟成功则返回开辟空间的 void* 类型的首地址,否则返回空指针,因此也需要对其返回值进行检查。

image.png

同样,我们用 calloc 开辟十个 int 类型大小的空间(一样是40),并观察整个开辟的空间。我们可以看到,空间内存中都是0,即calloc对所开辟的空间会将其初始化成0,避免发生意外的错误,规避风险。

image.png

free


我们知道,动态内存开辟出来的空间的作用域非常地宽广,为了避免内存浪费而造成内存的泄露,保证程序运行的速度始终在线。正所谓:“有借有还,再借不难。”所以通过 free 函数对开辟后不再使用的空间进行回收和释放。

image.png

free函数接收一个指针,将其释放并没有返回值。值得一提的是所传输的地址必须是申请空间的首地址,这样才能正常运行,因此建议不对原指针进行直接运算,而是用间接的方法来访问整个空间。同时,一个 malloc calloc 对应一个 free ,保证不使用就释放。

image.png

 这时候我们发现,ptr 还是存有某个位置的地址,但那个地址我们并没有使用的权力,即为野指针,若对其解引用操作就会导致错误的产生。所以我们还需要对这个指针进行制空的处理。

image.png

这样就可以保证 ptr 接下来不会对程序造成影响。这样处理过后才真正地完成动态内存的善后工作,一定要做到有申请就有释放,保证程序的正常运行而不会造成内存泄漏。

值得提一嘴的是,如果让 free 去释放一个定义的变量,那结果将取决于编译器,不同的编译器会有不同的结果。若释放一个空指针,free 则什么事都不会做。

realloc


讲到这里,可能有人想问了,不是动态内存吗?动呢?,前面那些定义下来好像也不会动吧。

026552c39dbc426da3643c40625e6198.gif

别着急,这不就来了吗。

image.png

realloc 这个函数就负责动态内存的动,可以对动态内存的空间的大小进行修改。需要输入一个动态内存的地址,后面的参数为修改后这片空间所占的单位是字节的大小(切记不是增加或减小的大小)。并且返回一个void*指针或空指针所以也必须对返回值检查,若创建失败没有检查就将原指针置换成空指针,就连原来空间的使用权也丧失了,如此便得不偿失了。

image.png

image.png

如此我们将ptr所指向的空间大小增大到80,从上图我们是可以看出来realloc是也是不会对开辟的空间进行初始化。

值得一提的是,在 realloc 开辟空间时会出现两种情况,第一种,当原来开辟空间的后面足够我们需要的,便会在原空间之后继续申请空间使用。

image.png

image.png

若原空间之后没有足够的空间让新申请的空间使用,则会选择一块新的足够大的空间供程序员使用,同时将原地址释放。

image.png

从上图便可以看到前后两个地址是不同的,也印证了前面说的系统会寻找一个全新的空间来代替原来的空间。

于是从定义上我们就可以看出,realloc(NULL,40) 与 malloc(40) 二者是等价的。

常见错误


前面说过,一个申请对应一个释放,但只是如此就能避免数据泄露吗?

image.png

如上代码,我们确实做到了一申请一释放,但实际上的结果好像并不如我们所愿。函数进入一个选择语句后就返回了,而p却没有被释放并制空。所以不仅仅是形式上要有一申请一释放而且逻辑上也要做到一申请一释放。


这样,关于动态内存的介绍就到此为止了,祝愿每位学习C语言的同学们都能凭自己努力而取得蜕变。


目录
相关文章
|
Unix 程序员 Linux
【OSTEP】动态内存开辟 | 内存API常见错误 | UNIX: brk/sbrk 系统调用 | mmap创建匿名映射区域 | mmap创建以文件为基础的映射区域
【OSTEP】动态内存开辟 | 内存API常见错误 | UNIX: brk/sbrk 系统调用 | mmap创建匿名映射区域 | mmap创建以文件为基础的映射区域
263 0
|
5月前
|
C语言
【C语言】:总结动态内存的常见错误
【C语言】:总结动态内存的常见错误
30 0
|
3月前
|
存储 程序员 Python
Python类的定义_类和对象的关系_对象的内存模型
通过类的定义来创建对象,我们可以应用面向对象编程(OOP)的原则,例如封装、继承和多态,这些原则帮助程序员构建可复用的代码和模块化的系统。Python语言支持这样的OOP特性,使其成为强大而灵活的编程语言,适用于各种软件开发项目。
32 1
|
4月前
|
设计模式 安全 Java
Java面试题:设计模式如单例模式、工厂模式、观察者模式等在多线程环境下线程安全问题,Java内存模型定义了线程如何与内存交互,包括原子性、可见性、有序性,并发框架提供了更高层次的并发任务处理能力
Java面试题:设计模式如单例模式、工厂模式、观察者模式等在多线程环境下线程安全问题,Java内存模型定义了线程如何与内存交互,包括原子性、可见性、有序性,并发框架提供了更高层次的并发任务处理能力
73 1
|
4月前
|
存储 JavaScript 前端开发
面试官:JS中变量定义时内存有什么变化?
面试官:JS中变量定义时内存有什么变化?
40 0
|
编译器 C语言
【C语言】动态内存分配malloc,realloc等函数使用和常见错误(上)
【C语言】动态内存分配malloc,realloc等函数使用和常见错误(上)
89 0
动态内存分布——malloc,calloc,realloc,free的使用。以及关于动态内存的常见错误。
动态内存分布——malloc,calloc,realloc,free的使用。以及关于动态内存的常见错误。
|
C语言
【C语言】动态内存分配malloc,realloc等函数使用和常见错误(下)
【C语言】动态内存分配malloc,realloc等函数使用和常见错误(下)
132 0
动态内存分配— —常见错误总结
动态内存分配— —常见错误总结
|
程序员 编译器 C语言
【C】动态内存管理(相关函数、常见错误、笔试题)
【C】动态内存管理(相关函数、常见错误、笔试题)
56 0
【C】动态内存管理(相关函数、常见错误、笔试题)