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

简介: 【C语言】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()函数详解能对大家有所帮助,欢迎大佬们留言或私信与我交流.

有关更多动态开辟相关知识可以移步:

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



C语言动态内存开辟相关库函数思维导图:


相关文章
|
4天前
|
传感器 人工智能 物联网
C 语言在计算机科学中尤其在硬件交互方面占据重要地位。本文探讨了 C 语言与硬件交互的主要方法,包括直接访问硬件寄存器、中断处理、I/O 端口操作、内存映射 I/O 和设备驱动程序开发
C 语言在计算机科学中尤其在硬件交互方面占据重要地位。本文探讨了 C 语言与硬件交互的主要方法,包括直接访问硬件寄存器、中断处理、I/O 端口操作、内存映射 I/O 和设备驱动程序开发,以及面临的挑战和未来趋势,旨在帮助读者深入了解并掌握这些关键技术。
25 6
|
11天前
|
存储 C语言
C语言如何使用结构体和指针来操作动态分配的内存
在C语言中,通过定义结构体并使用指向该结构体的指针,可以对动态分配的内存进行操作。首先利用 `malloc` 或 `calloc` 分配内存,然后通过指针访问和修改结构体成员,最后用 `free` 释放内存,实现资源的有效管理。
57 12
|
3天前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
17 1
|
8天前
|
存储 C语言 计算机视觉
在C语言中指针数组和数组指针在动态内存分配中的应用
在C语言中,指针数组和数组指针均可用于动态内存分配。指针数组是数组的每个元素都是指针,可用于指向多个动态分配的内存块;数组指针则指向一个数组,可动态分配和管理大型数据结构。两者结合使用,灵活高效地管理内存。
|
18天前
|
C语言
c语言调用的函数的声明
被调用的函数的声明: 一个函数调用另一个函数需具备的条件: 首先被调用的函数必须是已经存在的函数,即头文件中存在或已经定义过; 如果使用库函数,一般应该在本文件开头用#include命令将调用有关库函数时在所需要用到的信息“包含”到本文件中。.h文件是头文件所用的后缀。 如果使用用户自己定义的函数,而且该函数与使用它的函数在同一个文件中,一般还应该在主调函数中对被调用的函数做声明。 如果被调用的函数定义出现在主调函数之前可以不必声明。 如果已在所有函数定义之前,在函数的外部已做了函数声明,则在各个主调函数中不必多所调用的函数在做声明
31 6
|
1天前
|
存储 缓存 算法
【C语言】内存管理函数详细讲解
在C语言编程中,内存管理是至关重要的。动态内存分配函数允许程序在运行时请求和释放内存,这对于处理不确定大小的数据结构至关重要。以下是C语言内存管理函数的详细讲解,包括每个函数的功能、标准格式、示例代码、代码解释及其输出。
19 6
|
1月前
|
C语言
【c语言】动态内存管理
本文介绍了C语言中的动态内存管理,包括其必要性及相关的四个函数:`malloc`、``calloc``、`realloc`和`free`。`malloc`用于申请内存,`calloc`申请并初始化内存,`realloc`调整内存大小,`free`释放内存。文章还列举了常见的动态内存管理错误,如空指针解引用、越界访问、错误释放等,并提供了示例代码帮助理解。
43 3
|
1月前
|
存储 算法 程序员
C语言:库函数
C语言的库函数是预定义的函数,用于执行常见的编程任务,如输入输出、字符串处理、数学运算等。使用库函数可以简化编程工作,提高开发效率。C标准库提供了丰富的函数,满足各种需求。
|
2月前
|
存储 C语言
【c语言】字符串函数和内存函数
本文介绍了C语言中常用的字符串函数和内存函数,包括`strlen`、`strcpy`、`strcat`、`strcmp`、`strstr`、`strncpy`、`strncat`、`strncmp`、`strtok`、`memcpy`、`memmove`和`memset`等函数的使用方法及模拟实现。文章详细讲解了每个函数的功能、参数、返回值,并提供了具体的代码示例,帮助读者更好地理解和掌握这些函数的应用。
27 0
|
2月前
|
C语言 C++
C语言 之 内存函数
C语言 之 内存函数
36 3