【C语言】free()函数详解(动态内存释放函数)

举报
修修修也 发表于 2024/03/30 14:28:12 2024/03/30
【摘要】 有关C语言库函数free()函数的详解,内容包括:函数简介,参数及返回值详解,函数的具体使用方法以及free()函数常见使用误区等相关内容.

一.free()函数简介

我们先来看一下cplusplus.com - The C++ Resources Network 网站上free()函数的基本信息:

1.函数功能

可以看到,free()函数的功能是:释放以前由malloc(),calloc(),realloc()函数动态开辟的内存空间.使其可以重新被分配.

2.函数参数

该函数有1个参数,是:

void free (void* ptr);

void * ptr

参数的类型无类型指针(void*),它指向先前由malloc(),calloc(),realloc()动态分配的内存块,它的作用是告诉函数要释放的内存块的起始位置.

3.函数返回值

函数的返回值类型空(void),它表示函数在运行结束后不需要返回值.

4.函数头文件

该函数包含在头文件<stdlib.h>中.

二.free()函数的具体使用

free()函数的使用场景是:当我们先前使用了malloc(),calloc(),realloc()函数开辟了动态内存空间,我们在不再使用这块空间时就应该及时使用free()函数将它释放掉,以免造成内存泄漏.

内存泄漏:如果动态开辟的内存没有被释放,那么这些内存就会一直占用系统资源,从而导致内存泄漏。内存泄漏会导致程序运行速度变慢,甚至崩溃。

1.使用free()函数完成malloc()开辟空间的释放

如下,我们使用free()函数将malloc()开辟空间的释放掉:

给free()函数传入:malloc()函数动态开辟的指针(即p).

int main()
{
    int* p = (int*)malloc(sizeof(int)*10); //开辟10个整型大小空间
    if (p == NULL) //如果开辟失败,则打印错误原因
    {
        //打印错误原因的一个方式
        printf("%s\n", strerror(errno));
    }
    else
    {
        int i = 0;
        for (i = 0; i < 10; i++) //遍历赋值并打印这10个整型空间
        {
            *(p + i) = i;
            printf("%d ", *(p + i));
        }
        //可以正常使用p指针来操作这片空间了
    }
    free(p); //释放p的内存空间
    p = NULL; //将指针p置为NULL,防止其变成野指针
    
    return 0;
}

在vs编译器中运行查看结果:

虽然这里free()将malloc()动态开辟的内存释放后好像也没有发生什么变化,但如果我们因此不释放之前malloc()动态开辟的空间,那么这块空间就会一直占用着内存,既没有用,也没法让其他人使用.

还有一点需要注意的是,我们在free()完p指向的空间后,最好将p的内容置为NULL,否则这块空间已经被释放掉了,但你还保存着这块空间的地址,后续如果不小心再访问p的话,就会造成非法访问.


2.使用free()函数完成calloc()开辟空间的释放

如下,我们使用free()函数calloc()开辟空间的释放掉:

给free()函数传入:calloc()动态开辟的内存指针(即p).

int main()
{
    int* p = (int*)calloc(10,sizeof(int)); //开辟10个整型大小空间
    if (p == NULL) //如果开辟失败,则打印错误原因
    {
        //打印错误原因的一个方式
        printf("%s\n", strerror(errno));
    }
    else
    {
        int i = 0;
        for (i = 0; i < 10; i++) //遍历并打印这10个整型空间
        {
            printf("%d ", *(p + i));//因为calloc()开辟的空间会自动初始化,所以我们可以不初始化这块空间,直接打印
        }
        //可以正常使用p指针来操作这片空间了
    }

    free(p); //释放p的内存空间
    p = NULL; //将指针p置为NULL,防止其变成野指针

    return 0;
}


在vs编译器中运行查看结果:

释放calloc()开辟的空间和malloc()一样,看不出来必要性,但却是非常有必要的.

3.使用free()函数完成realloc()开辟空间的释放

如下,我们使用free()函数realloc()开辟空间的释放掉:

分别给free()函数传入:realloc()扩容后的内存块指针(即p).

int main()
{
int* p = (int*)calloc(10,sizeof(int)); //开辟10个整型大小空间
if (p == NULL) //如果开辟失败,则打印错误原因
{
//打印错误原因的一个方式
printf("%s\n", strerror(errno));
}
else
{
int i = 0;
for (i = 0; i < 10; i++) //遍历并打印这10个整型空间
{
printf("%d ", *(p + i));//因为calloc()开辟的空间会自动初始化,所以我们可以不初始化这块空间,直接打印
}
//可以正常使用p指针来操作这片空间了
}
printf("\n");
p = (int*)realloc(p, 15 * sizeof(int));//将这块空间扩容到15个整型
if (p == NULL)
{
printf("%s\n", strerror(errno));
}
else
{
int i = 0;
for (i = 0; i < 15; i++) //遍历并打印这15个整型空间
{
printf("%d ", *(p + i));
}
}
free(p); //释放p的内存空间
p = NULL; //将指针p置为NULL,防止其变成野指针
return 0;
}

在vs编译器中运行查看结果:

realloc()扩容的内存块,扩容前的数据会保留,但新扩容的空间不会初始化,因此后面五个元素打印出的值是随机值.


三.free()函数常见使用误区

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

因为p是由编译器分配到栈区的,不属于堆区,因此不能使用free释放.

void test()
{
int a = 10;
int *p = &a;
free(p); //p不是动态开辟的,不能释放
}

 使用vs2022测试一下:

可以看到,该错误导致了程序出错.

而图中的报错"已执行断点指令"则是因为代码执行过程中出现了未定义的非法行为.


2.使用free释放一块动态内存的一部分

如下代码:

void test()
{
int *p = (int *)malloc(100);
p++;
free(p); //p不再指向动态内存的起始位置
}

 在vs2022中测试一下:

可以看到,该错误导致了程序异常终止.


3.对同一块动态内存多次释放

如下代码:

void test()
{
int *p = (int *)malloc(100);
free(p);
free(p); //重复释放
}

 在vs2022中进行测试:

可以看到,该错误导致了程序出错.

这里列出了两个防止重复释放的小技巧:

在设计时尽量遵从:谁开辟,谁回收的原则

在free完后立刻将原动态开辟的指针置为NULL.


4.动态开辟内存后忘记释放

如下代码:

void test()
{
int *p = (int *)malloc(100);
if(NULL != p)
{
*p = 20;
}
//没有释放!
}

int main()
{
test();
}

 如果动态开辟的内存忘记释放,程序不会报错,但会造成内存泄漏!

忘记释放不再使用的动态开辟的空间会造成内存泄漏.

内存泄漏:如果动态开辟的内存没有被释放,那么这些内存就会一直占用系统资源,从而导致内存泄漏。内存泄漏会导致程序运行速度变慢,甚至崩溃。

因此:

动态开辟的空间一定要释放,并且正确释放!

动态开辟的空间一定要释放,并且正确释放!

动态开辟的空间一定要释放,并且正确释放!


结语

希望这篇free()函数详解能对大家有所帮助,欢迎大佬们留言或私信与我交流.

学海漫浩浩,我亦苦作舟!关注我,大家一起学习,一起进步!

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。