C语言——动态内存管理

大家好,本期和大家分享C语言动态内存管理有关知识,记得三连支持一下哦!

2f36ea34de3940548aab5862d5eb1b2c.jpeg

一、为什么要有动态内存分配

我们目前已经知道的内存开辟方式有两种,分别是:定义变量和开辟数组空间。

这两种方式都有共同的特点:

1.内存空间都是在栈空间上开辟的;

2.开辟的空间大小是固定的,数组在声明的时候,必须指定数组的⻓度,数组空间⼀旦确定了⼤⼩不能调整。

但是我们在编程时对于空间的需求往往是在程序运⾏的时候才能知道,那么开辟的空间一开始就固定的话,这对我们编程是有很多局限性的,所以,在C语言中程序员可以动态的申请空间和释放空间,这就大大的增加了程序的灵活性。

二、malloc、calloc、realloc和free

我们要想动态的开辟空间和释放空间,就必须用到以下几个函数:

malloc、calloc、realloc和free

下面我们就依次来介绍。

1.malloc 

malloc是一个库函数,这个函数向内存申请⼀块连续可⽤的空间,并返回指向这块空间的指针。

它有以下特点:

1.如果开辟成功,则返回⼀个指向开辟好空间的指针;
2.如果开辟失败,则返回⼀个 NULL 指针,因此malloc的返回值⼀定要做检查;
3.返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使⽤的时候使⽤者⾃⼰来决定;
4.如果参数 size 为0,malloc的⾏为是标准是未定义的,取决于编译器;

5.使用要包含头文件#include<stdlib.h>.

b25c0f8bd6504de88a89e4e4364cf867.png

如何使用呢?我们来看例子:c15ca426be6d4e5d88350cee86619f04.png

上面这段代码是以后我们经常会用到的申请空间的方式,肯定有许多小伙伴们不太理解,下面我就来讲解一下:6adc8d48ff8649049f8ea5186e01a8ec.png

在使用malloc函数是我们要注意:

1.包含头文件#include<stdlib.h>;

2.强制转换指针类型;

3.判断空间是否成功开辟;

4.使用完后要手动释放并将使用的指针置空。

2.calloc

和malloc一样,calloc也是一个开辟空间的库函数,唯一不同的是,calloc在返回地址前会把开辟的空间每个字节都初始化为0。

18c38453b2e54b60803248bc6de6a7b0.png

下面我们来看个例子:b84a64f6f5844b3492ce662eb1318220.png

calloc的使用注意事项和malloc一样,这里不再赘述。

3.realloc

realloc也是一个开辟空间的库函数,功能和malloc相似,但是又有不同之处。有时我们在开辟空间的时候也不知道具体需要多大的的空间,开大了还不要紧,但是开小了就比较麻烦了,这个时候我们就可以使用realloc来解决这个问题。realloc 函数就可以做到对动态开辟内存⼤⼩的调整。

ce6e0646148b4724ba493ba721464915.png

1.ptr是要调整的内存地址;
2.size 调整之后新⼤小;
3.返回值为调整之后的内存起始位置;
4.这个函数调整原内存空间⼤⼩的基础上,还会将原来内存中的数据移动到新的空间。

所以realloc在调整内存空间的是存在两种情况:

(1)原有空间之后有⾜够⼤的空间:
要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发⽣变化;

(2)原有空间之后没有⾜够⼤的空间:
原有空间之后没有⾜够多的空间时,扩展的⽅法是:在堆空间上另找⼀个合适⼤⼩
的连续空间来使⽤。这样函数返回的是⼀个新的内存地址。

下面我们画图来理解一下:

a67822de9d054f858fbb9cb9ad245547.png

在这里我们还要注意的是:情况(2)中新空间开辟之后会把原有空间中的数据拷贝到新空间中。

所以说realloc函数的功能还是很强大的。功能强大那么我们使用时需要注意的地方就更多,下面我们就来看看例子:

539146e2b0ba430b9d563dbc7281b029.png

我们看这段代码好像没有什么问题,但它存在着一定的缺陷:

我们思考一下如果realloc扩容失败会怎么样?

上面说过realloc扩容失败会返回空指针,这就会导致前面的数据全都丢失了,这就非常得不偿失了。所以我们应该这样优化:

0fd3d8d9559046f49be54de4d8602216.png

这样优化就能防止上面的情况出现。

当然,在通常情况下,realloc是可以当成malloc使用的

4.free

free是一个释放空间的库函数,在上面我们也使用过了,下面我们具体来介绍一下。

d879899766f24600a970869314414850.png

free函数⽤来释放动态开辟的内存。
如果参数 ptr 指向的空间不是动态开辟的,那free函数的⾏为是未定义的。
如果参数 ptr 是NULL指针,则函数什么事都不做。

注意:malloc、calloc、realloc主动申请的空间都需要我们手动释放,如果不主动释放,除了作用域不会自动销毁就可能导致内存泄漏。

三、常⻅的动态内存的错误

下面我们来看一些动态内存常见的错误

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

115f83bf37c647c0ad8ee7e9bb399b56.pngf2271fc9758343b288aa00b86a0a0d81.png

我们可以看到上面两段代码都警告说取消对空指针的引用,第一段代码当然运行时会崩掉,第二段代码即使运行出来了也只是没有遇到极端情况,遇到极端情况也是会崩的。

eb59d1ef56c3499eb6ecfbe5b5ec2d93.png

所以我们通常在使用开辟空间这些函数后要检查一下是否开辟成功,防止传回空指针导致下面的程序出错。

我们在使用指针时也要注意不要对空指针解引用。

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

对动态开辟空间的越界访问和数组越界访问相似,都是访问到不属于你的空间,这个时候往往会导致报错。

8db793fadf1846ef8e85bfc975ca11c9.png

我们看到在VS上没有报错,这是因为VS环境还是比较友好的,但也报了警告,在其他环境下就不一定了。所以无论是数组还是动态开辟的空间我们都要控制好数据,防止越界访问。

3.对⾮动态开辟内存使⽤free释放

我们知道我们定义的变量系统会自己分配空间,当出了作用域时系统会自己销毁,不需要我们自己手动销毁。如果我们去干别人的事,就会出现问题。如:

e2f5ee40457640c48f9c165a88c99ecd.png

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

我们在做一件事的时候要去做完才算成功,free释放空间也是这样的。只释放一部分也会出现问题,如:

f6f94e4240ba4d19a5500ac944ae716e.png

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

当然一块空间释放完了,我们就不需要再去释放一遍,否则就会出现错误。

0e1c08af2c334825a77f98ce28f2a21d.png

6.动态开辟内存忘记释放

这个问题我们上面也多次强调过,动态开辟的内存一定要手动释放掉,否则会导致内存泄漏。

一次我们可能察觉不到,但是在一个项目里,我们每次动态开辟的空间都不释放,到后面累计的到一定程度将会成为一个大问题。

所以:切记,动态开辟的空间⼀定要释放,并且正确释放。

四、C语言中程序内存区域划分

在上面我们提到栈、堆等等到底是什么呢?其实它们是内存中的区域划分。下面就来具体介绍一下C语言中内存区域是如何划分的。

8bf86cd6991b466db1f3d4ed554b2245.png

1. 栈区(stack):在执⾏函数时,函数内局部变量的存储单元都可以在栈上创建,函数执⾏结束时这些存储单元⾃动被释放。栈内存分配运算内置于处理器的指令集中,效率很⾼,但是分配的内存容量有限。栈区主要存放运⾏函数⽽分配的局部变量、函数参数、返回数据、返回地址等;
2. 堆区(heap):⼀般由程序员分配释放,若程序员不释放,程序结束时可能由系统回收。分配⽅式类似于链表;
3. 数据段(静态区)(static)存放全局变量、静态数据。程序结束后由系统释放;
4. 代码段:存放函数体(类成员函数和全局函数)的⼆进制代码。

好了,以上就是本篇文章的全部内容了,创作不易,记得三连支持一下哦!我们下期再见!

812dd1a160274e53b45d2b148cd7ba71.gif

  • 27
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
你好!很高兴回答你的问题。关于软件安全实验二中的攻击方式Pwn,我可以为你提供一些基本的信息。Pwn是指通过利用软件漏洞来获取对计算机系统的控制权。在实验二中,你可能会学习和尝试使用缓冲区溢出漏洞、格式化字符串漏洞、堆溢出等技术来进行Pwn攻击。 缓冲区溢出是一种常见的Pwn攻击技术,它利用了程序在处理输入数据时没有正确限制长度的漏洞。通过向程序输入过长的数据,可以覆盖到程序运行时的内存空间中的其他重要数据,例如返回地址。通过修改返回地址,攻击者可以控制程序流程,执行恶意代码。 格式化字符串漏洞是另一种常见的Pwn攻击技术。它利用了C语言的格式化字符串函数(如printf、sprintf等)在处理格式化字符串时存在的安全问题。通过向程序输入特定格式的字符串,攻击者可以读取或修改内存中的数据,甚至执行任意代码。 堆溢出是利用堆内存管理中的漏洞进行攻击的一种技术。在使用动态分配内存时,如果没有正确地释放或管理内存,可能会导致堆溢出。通过在堆中溢出写入数据,攻击者可以修改关键数据结构,从而影响程序的执行逻辑。 以上只是Pwn攻击的一些基本概念,实际的Pwn攻击还涉及很多技术和细节。在进行任何Pwn攻击之前,请务必遵循法律和道德规范,并确保你在合法授权的环境中进行实验。 如果你有任何关于Pwn攻击或软件安全实验的具体问题,我会尽力为你解答。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值