目录
malloc和free
malloc
malloc
是 C 语言中用来动态分配内存的函数。通过 malloc
函数可以在程序运行时动态地分配指定大小的内存空间,这样可以灵活地管理内存并在需要时分配所需大小的内存空间。
一般的 malloc
函数的原型为:void *malloc(size_t size);
size
参数表示需要分配的内存空间的大小,以字节为单位。malloc
函数会返回一个指向分配的内存空间起始位置的指针,如果分配失败则返回NULL
。
使用 malloc
函数动态分配内存后,需要在程序结束前通过 free
函数释放已分配的内存,以避免内存泄漏问题。
因为有可能开辟失败,因此malloc的函数一定要做检查(判断是否为空)。另外,函数的返回类型是void*,使用时需要根据自己需要的类型进行强制类型转换。
free
free
是 C 语言中用来释放动态分配的内存空间的函数。通过 free
函数可以将之前通过 malloc
、calloc
、realloc
等函数分配的内存空间释放,以便系统可以重新利用这些内存空间。
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
:指向之前由malloc
,calloc
或realloc
返回的指针,或者是NULL
。size
:新的内存块大小(以字节为单位)。
realloc
函数可以用于扩大或缩小先前动态分配的内存块的大小。具体而言,它做了以下几件事情:
- 如果
ptr
是NULL
,则它的行为类似于malloc(size)
,即分配一个新的大小为size
字节的内存块,并返回指向此内存块的指针。 - 如果
size
是零,且ptr
不是NULL
,则它的行为类似于free(ptr)
,即释放先前分配的内存,并返回NULL
。 - 如果
ptr
不是NULL
且size
不为零,它会尝试重新分配ptr
指向的内存块,并将其大小调整为size
字节。如果成功,它返回指向调整大小后内存块的指针;如果失败,它保持原有内存块不变,并返回NULL
。
需要注意的是,由于 realloc
可能会在内存重新分配时将数据从旧内存复制到新内存,因此在重新分配大内存块时可能会涉及到性能开销。此外,对于重新分配失败的情况,原先的内存块仍然保持有效,因此在调用 realloc
后,需要检查返回值来确保内存是否被成功重新分配。
realloc开辟空间会有两种开辟方式
第一种情况是原空间所在处后面的空间不够realloc新开辟的大小,于是重新在另一处足够大的空间处开辟新的空间。
第二种情况是后面的空间够用,能够放下新realloc出的空间,于是在原空间的基础上加长。
情况1
名称 | 值 | 类型 | |
---|---|---|---|
▶ | p | 0x00000208f0b31f90 {0} | int * |
名称 | 值 | 类型 | |
---|---|---|---|
▶ | ptr | 0x00000208f0b2a750 {0} | int * |
情况2
名称 | 值 | 类型 | |
---|---|---|---|
▶ | p | 0x000001790ad2a700 {0} | int * |
名称 | 值 | 类型 | |
---|---|---|---|
▶ | ptr | 0x000001790ad2a700 {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
是一个柔性数组成员,它没有指定数组的大小,这样就可以根据需要动态调整数组的大小。在使用柔性数组时,需要注意以下几点:
- 柔性数组必须是结构体的最后一个成员,且不能有其他成员跟在其后。
- 结构体中的柔性数组成员不能被初始化,也不能作为参数传递给函数。
- 动态分配包含柔性数组的结构体时,需要额外考虑柔性数组的大小。
创建的数组data[],方框里填0、填100或不填都无所谓,本质上要的是指针,有了这个指针配合内存分配函数(malloc,calloc,realloc之类)就可以指向一块空间。
发现没有,结构体的大小为4,数组的大小并没有被计算,需要额外考虑。
使用柔性数组时,额外创建数组空间
struct st* ps = (struct st*)malloc(sizeof(struct st) + 10 * sizeof(int));
要用结构体指针来接收
柔性数组空间的释放
应先释放柔性数组部分后释放创建的ps