没有使用malloc或calloc的free()函数

5

快速问题

在不先调用malloc的情况下,是否可以使用free()函数?

例如。

void someFunc( void )
{
   char str[6] = {"Hello"};

   //some processing here ....

   free(str);
}

我没有遇到编译错误,但这个代码是否能够正确工作?非常感谢。

实际上,您可以在不调用malloc的情况下使用free,但前提是您传递给free的值必须是空指针。因此,如果您想要一个指针,它可能指向已分配的块,但可能指向本地数组,则无法使用。 - Steve Jessop
6个回答

11

这完全不正确:

  1. 你不能释放静态数组,比如char str[6]
  2. 只能在你分配的内存上(或空指针上)才应该调用free()。

谢谢,我会在11分钟内检查您的答案。 - jramirez
还要注意,正如@Emil H所说,str将在堆栈上分配,并且在函数返回时将自动释放。是的,使用0或NULL或空指针调用free()并不是问题。 - Zabba

6
当您调用malloc()或任何其他分配函数时,内存将在堆上分配。这是唯一可以释放的内存。当您声明静态字符串时,就像在您的示例中所做的那样,该字符串会在另一个内存段中在编译时分配。对于str指针本身也是如此,它在堆栈上分配,因此也不能被释放。

3

在未malloc的变量上使用free通常会导致段错误。例如:

#include <stdlib.h>

int main()
{
  char str[6] = {"Hello"};
  free(str);
}

$ gcc test.c -o test

$ ./test

分段错误


3
这真的是未定义的行为。寻找这些小错误最好的工具是valgrind。 - Yann Ramin
UB UB UB!:-) 不过这是个很好的“为什么这很糟糕”的实际例子 :p - user166390
1
如果你很幸运的话,会出现段错误(Seg fault),但只有在某些平台上。实际行为取决于在释放地址之前存在的垃圾数据,其中一个真正的malloc块具有其堆管理结构。在最坏的情况下,不会发生运行时错误,但由于堆管理数据已损坏,随后的malloc()将失败。 - Clifford

3

free()使用在分配块之前的数据来管理堆。如果指向的内存不是通过像malloc()或calloc()这样的堆分配函数分配的,则块之前的数据将作为堆管理数据毫无意义。

一些库会检测无效的堆数据并产生运行时错误,否则行为是未定义的。通常这种错误的后果只有在稍后尝试分配更多内存时才会被注意到。这使得调试此类错误非常困难。

您不会得到编译器错误,因为它不是语法错误,也不可在编译时检测到。编译器不知道库函数的语义。它所知道的只是malloc()返回一个void*,free()接受一个void*;在编译时没有办法知道指针是否指向动态分配的块,因为内存按定义是在运行时分配的。此外,指针可以在运行时修改以指向任何类型的内存,或者可以别名-复制到另一个指针,然后通过第二个指针进行释放。如果您期望编译器提供错误消息,那么您对编译器的期望很高;但是某些静态分析工具可能能够警告此类错误可能发生,而动态分析工具(如valgrind)可能会在测试期间检测到错误。


0
你不能这样做,但其实也没必要。当你在函数内部声明一个变量时,它被称为自动变量,因为它会在函数结束时自动删除(释放其内存)。
如果你想限制自动变量的生命周期,可以使用{}引入作用域,像这样:
void someFunc( void )
{
    // do some stiff here ...

    { // <- introduce a temporary scope

        char str[6] = {"Hello"}; // this is local to your new scope

        // some processing here ...

    } // <- your array str[] is destroyed here

    // do some more stuff here, str[] has disappeared

}

为了清晰起见,没有额外的注释:

void someFunc( void )
{
    // do some stiff here ...

    {

        char str[6] = {"Hello"};

        // some processing here ...

    }

    // do some more stuff here ...

}

在函数内部引入新的作用域是一种常见的习惯用法,它可以清理掉广泛作用域中的多余变量(可能是 bug 的潜在来源),同时也可以利用 RAII 的强大功能。

例如,在同步函数代码的一小部分时锁定 mutex


0


free(3)函数接受一个void *参数,因此您可以传递任何类型的指针而不会出现编译时错误。但是,如果指针最初不是由malloc(3)返回并且以前从未返回给free(3),则会发生糟糕的事情。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接