动态内存管理

本文详细介绍了C语言中的内存管理函数malloc、calloc、realloc,以及柔性数组的使用方法和注意事项,包括内存分配、初始化、动态调整、内存释放和避免常见错误,如野指针和错误的内存释放顺序。
摘要由CSDN通过智能技术生成

目录

malloc和free

malloc

free

使用部分

calloc和realloc

calloc

realloc

realloc开辟空间会有两种开辟方式

使用部分

避坑

柔性数组

柔性数组空间的释放


malloc和free

malloc

malloc 是 C 语言中用来动态分配内存的函数。通过 malloc 函数可以在程序运行时动态地分配指定大小的内存空间,这样可以灵活地管理内存并在需要时分配所需大小的内存空间。

一般的 malloc 函数的原型为:void *malloc(size_t size);

  • size 参数表示需要分配的内存空间的大小,以字节为单位。
  • malloc 函数会返回一个指向分配的内存空间起始位置的指针,如果分配失败则返回 NULL

使用 malloc 函数动态分配内存后,需要在程序结束前通过 free 函数释放已分配的内存,以避免内存泄漏问题。

因为有可能开辟失败,因此malloc的函数一定要做检查(判断是否为空)。另外,函数的返回类型是void*,使用时需要根据自己需要的类型进行强制类型转换。

free

free 是 C 语言中用来释放动态分配的内存空间的函数。通过 free 函数可以将之前通过 malloccallocrealloc 等函数分配的内存空间释放,以便系统可以重新利用这些内存空间。

free 函数的原型为:void free(void *ptr);

  • ptr 参数是一个指向之前分配的内存空间起始位置的指针。
  • 调用 free 函数会将之前分配的内存空间释放,并标记为可供其他程序使用。

需要注意的是,一旦调用了 free 函数释放了某个内存空间,就不应再使用该内存空间,否则可能会导致程序运行出错。

使用free不仅要求传递的是起始位置的指针还要求是动态内存分配的空间。虽然free会释放空间,将这块空间还给堆区,但p还是指向着原来的那一块地址,需要手动置空。

使用部分

如果使用free后没有把指针置空就会出现野指针,这里p就成为了野指针。

p置空后,就不存在野指针的问题了

上述代码仍有问题,根据malloc函数的介绍,开辟空间是有可能失败的,所以一定要紧跟着判断一下。

calloc和realloc

calloc

calloc 是 C 语言中用于动态分配内存并初始化为零的函数。它的函数原型如下:

void *calloc(size_t num, size_t size);

  • num:要分配的元素个数。
  • size:每个元素的大小(以字节为单位)。

calloc 函数会分配一个大小为 (num * size) 字节的内存块,并将分配的内存块中的每个字节都初始化为零。这使得 calloc 在需要初始化内存内容为零时非常有用,例如在数组、结构体等数据结构的动态分配中使用。

malloc 不同,calloc 在分配内存时会自动初始化为零,而不需要额外的循环或赋值操作来实现清零的功能。

当您使用 calloc 分配内存后,记得在使用完毕后通过 free 函数来释放动态分配的内存,以避免内存泄漏问题。

realloc

realloc 函数是 C 语言中用于重新分配动态内存大小的函数。它的函数原型如下:

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

  • ptr:指向之前由 malloccalloc 或 realloc 返回的指针,或者是 NULL
  • size:新的内存块大小(以字节为单位)。

realloc 函数可以用于扩大或缩小先前动态分配的内存块的大小。具体而言,它做了以下几件事情:

  1. 如果 ptr 是 NULL,则它的行为类似于 malloc(size),即分配一个新的大小为 size 字节的内存块,并返回指向此内存块的指针。
  2. 如果 size 是零,且 ptr 不是 NULL,则它的行为类似于 free(ptr),即释放先前分配的内存,并返回 NULL
  3. 如果 ptr 不是 NULL 且 size 不为零,它会尝试重新分配 ptr 指向的内存块,并将其大小调整为 size 字节。如果成功,它返回指向调整大小后内存块的指针;如果失败,它保持原有内存块不变,并返回 NULL

需要注意的是,由于 realloc 可能会在内存重新分配时将数据从旧内存复制到新内存,因此在重新分配大内存块时可能会涉及到性能开销。此外,对于重新分配失败的情况,原先的内存块仍然保持有效,因此在调用 realloc 后,需要检查返回值来确保内存是否被成功重新分配。

realloc开辟空间会有两种开辟方式

第一种情况是原空间所在处后面的空间不够realloc新开辟的大小,于是重新在另一处足够大的空间处开辟新的空间。

第二种情况是后面的空间够用,能够放下新realloc出的空间,于是在原空间的基础上加长。

情况1

名称类型
p0x00000208f0b31f90 {0}int *
名称类型
ptr0x00000208f0b2a750 {0}int *

情况2

名称类型
p0x000001790ad2a700 {0}int *
名称类型
ptr0x000001790ad2a700 {0}int *

使用部分

使用malloc开辟的空间如果不赋初值就会打印一堆乱码,而使用calloc开辟的空间会自动初始化为0

realloc开辟的空间不能直接接收需要一个临时变量来判断一下,因为realloc可能会开辟空间失败。

realloc用NULL开辟空间时作用与calloc是等效的

int main()
{
    int* p = (int*)realloc(NULL, 40);
    free(p);
    p = NULL;
    return 0;
}

避坑

1.对非动态开辟空间使用free释放

在敲代码的过程中,可能不小心就把一些非动态内存空间赋给p了之类的,过后运行就发现程序崩了。

出现这样的字眼就说明内存释放有问题。

2.free释放的必须是初始地址

否则还是崩

3.还有一个很重要的点,在使用构造的函数时,虽然给函数传递的是指针的名,但会在函数中会被认为是形参,必须要&(取地址)传递的才是实参,即使指针名的类型是int*之类带着*的。

例:

即使str是char*类型,并且传入后用p接收并开辟了空间,后用strcpy函数复制字符串,但是,p是形参,是临时变量,在函数结束时就已然销毁,str依旧是NULL。

4.在函数内部创建的空间会随着函数的结束从而销毁空间,虽然在函数内部成功创建了空间并return给了出去,但是空间已经销毁,销毁后得到是栈空间的地址。这里的临时空间也就是临时变量,可用static将临时变量转换成全局变量,将空间转移到静态空间。

例:
虽然preturn给了str,但str依旧是空指针

如果用static修饰

5.当free释放后未置空时

例:

虽然还能运行,但已经非法访问了。

柔性数组

在 C 语言中,柔性数组(Flexible Array Member)是一种特殊的结构体成员,它允许在结构体的最后定义一个长度不定的数组。柔性数组成员在 C99 标准中引入,并通常用于实现变长数据结构,如动态数组。

柔性数组的定义方式如下:

struct flex_array

{
    int length;
    int data[]; // 柔性数组成员,可以用来存储变长数据
};

在这个例子中,data 是一个柔性数组成员,它没有指定数组的大小,这样就可以根据需要动态调整数组的大小。在使用柔性数组时,需要注意以下几点:

  1. 柔性数组必须是结构体的最后一个成员,且不能有其他成员跟在其后。
  2. 结构体中的柔性数组成员不能被初始化,也不能作为参数传递给函数。
  3. 动态分配包含柔性数组的结构体时,需要额外考虑柔性数组的大小。

创建的数组data[],方框里填0、填100或不填都无所谓,本质上要的是指针,有了这个指针配合内存分配函数(malloc,calloc,realloc之类)就可以指向一块空间。

发现没有,结构体的大小为4,数组的大小并没有被计算,需要额外考虑。

使用柔性数组时,额外创建数组空间

struct st* ps = (struct st*)malloc(sizeof(struct st) + 10 * sizeof(int));

要用结构体指针来接收

柔性数组空间的释放

应先释放柔性数组部分后释放创建的ps

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值