动态内存管理

在之前我们将一些我们看到的东西学习了,今天我们再来学习一些内存里的那些东西吧

文章目录


前言

在我们使用C语言的时候,它将一些工具已经制作好了,空间分配好了。在上一节中我们学习了一些自定义数据类型,我们可以根据自己的需求来写出相应的数据类型。今天我们再来学习一种自己分配内存空间的方法——动态内存管理。


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

 我们知道我们之前所学习的int,char,double等等数据类型以及数组,我们在定义变量的时候,编译器就自己给它分配好了空间。但是有时候又不太方便,有时候内存空间大了会造成内存浪费,内存空间小了又不能存储想要的内容,可谓是进退两难。于是C语言引入了动态内存开辟,让程序员自己开辟,释放内存空间,那样就灵活多了。

二、动态内存管理中的一些函数

2.1 malloc

void* malloc (size_t size);

上述代码是malloc函数的原型:这个函数返回的是一个泛型指针类型,我们在使用的时候可以通过强制转换,将其转换为我们想要的数据类型。其次()中的是我们申请创建动态内存空间的大小,单位是字节。

这个函数在创建之后,其内存空间里的值是一些随机值。

2.2 free

void free (void* ptr);

上述代码是free函数的原型:这个函数无相应类型,()放的是想要释放的内存空间其对应的指针。

这个函数是专门用来对动态内存进行释放和回收的函数

free函数用来释放动态开辟的内存。

• 如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的。

• 如果参数 ptr 是NULL指针,则函数什么事都不做。

2.3 calloc

void* calloc (size_t num, size_t size);

上述代码是这个函数的原型:这个函数返回的也是,与malloc一样。()中的num是我们想要开辟几个数据类型的空间的个数,size就是我们想要创建的数据类型的大小,单位字节。

calloc与malloc函数的区别就在于calloc会在返回地址之前把申请的空间的每个字节初始化为全 0,malloc中的每个字节是个随机值。

所以如果我们对申请的内存空间的内容要求初始化,那么可以很方便的使用calloc函数来完成任务。

2.4 realloc

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

上述代码是这个函数的原型:这个函数的返回类型也是泛型指针类型,()中第一个参数是我们想要调整的那个动态内存空间起始位置的指针,第二个参数是我们想要调整的字节数。

realloc函数的出现让动态内存管理更加灵活。 有时会我们发现过去申请的空间太小了,有时候我们又会觉得申请的空间过大了,那为了合理的使用内存,我们⼀定会对内存的大小做灵活的调整。那 realloc 函数就可以做到对动态开辟内存大小的调整。

在使用这个函数之前,我们要想考虑一下:

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

◦ 情况1:原有空间之后有足够大的空间

◦ 情况2:原有空间之后没有足够大的空间

说是有两种情况,在实际书写代码的时候我们其实不用想那么多,我们只要在将调整之后的空间起始位置的指针赋给原空间起始位置的指针时加一个判断就好了。

三、常见的一些动态内存的错误

3.1 对NULL进行解引用操作

我们在创建动态空间之后,我们不知道其起始位置的指针是否是个NULL,如果贸然直接将其进行解引用操作,就可能犯这种错误。

因此我们为了避免这种事情发生,我们要创建动态空间之后及时进行检验。

 

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

我们知道数组是一开始就确定其数组长度了,后面是无法更改的。我们开辟的动态空间虽然后续可以进行一些调整,但是在第一次申请空间之后,我们访问的时候越界了,这样没能及时确定动态开辟空间的长度,导致其越界访问。

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

我们之前所学习的那些数据类型,数值都是非动态开辟的内存,而free是一种专门针对动态空间的函数,因此无法对齐进行释放。

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

free函数接收的是动态空间起始位置的指针,如果我们没能将动态空间的起始位置的指针传给free函数,只将动态空间中的某一位置的指针传给free,那么free函数就只能释放一块动态开辟内存的一部分。

3.5 对同一动态内存进行多次释放

在C语言中,对同一块动态分配的内存进行多次释放会导致未定义行为。这可能会导致程序崩溃、内存损坏,或者其他不可预测的问题。确保每块内存只释放一次是良好的编程实践。

3.6 动态开辟内存忘记释放(内存泄漏)

在C语言中,忘记释放动态分配的内存会导致内存泄漏。随着时间的推移,内存泄漏会积累,可能会耗尽系统的内存资源,导致程序变慢、崩溃,或影响系统的整体性能。

四、柔性数组

我们在刚开始学习C语言不久就接触到了数组,我们知道数组可以存储一些相同类型的数据,但是它的长度是一出生就被卡死的,因此我们无法在后续的操作中进行对数组长度的调整。但是,我们今天学习的动态内存管理与我们即将要学习的柔性数组配合起来将有奇效。

柔性数组:C99中,结构中的最后⼀个元素允许是未知大小的数组,这就叫做『柔性数组』成员。

struct s
{
	int a;
	int arr[];
	//int arr[0]
};

上述代码中的int arr[ ]与int arr[0] 就是我们要学习的柔性数组。

 柔性数组的特点:

• 结构中的柔性数组成员前⾯必须至少⼀个其他成员。

• sizeof返回的这种结构大小不包括柔性数组的内存。

• 包含柔性数组成员的结构用malloc()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。

我们在开始的时候,柔性数组是未知大小的,因此我们在计算结构体大小的时候就索性不计算柔性数组的大小。

如下是柔性数组的使用:

总结

在C语言中:

  • 内核空间:操作系统内核的内存区域,存放内核代码和数据。程序无法直接访问。
  • :用于存储局部变量、函数参数和返回地址。栈由编译器自动管理。
  • 内存映射段:用于映射文件到内存或动态链接库。通过mmap函数等进行管理。
  • :用于动态分配内存(如通过malloccallocrealloc)。程序员需要手动释放(free)。
  • 数据段:存储静态和全局变量,包括初始化的和未初始化的(BSS段)。在程序编译时分配。
  • 代码段:存储程序的可执行代码。由编译器生成,程序员无需直接管理。

这些内存区域通过操作系统和编译器自动管理和分配。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值