free()函数是否设置errno变量?

64
如果buf是通过malloc()分配的字符缓冲区,那么free(buf)是否会设置/重置errno
假设我想将缓冲区写入文件,然后释放它,因为我不再需要它。
假设代码的错误策略是在出错时返回-1。
这是一种正确的方法来写出缓冲区并进行错误检查,而不会泄漏内存吗?
fputs(buf, somefile);
free(buf);
if (errno) return -1;

还是说我需要考虑可能会设置errno的free函数,比如...

fputs(buf, somefile);
if (errno){ 
    free(buf);
    return -1;
}
free(buf);

或者,更可怕的是,

do { 
  fputs(buf, somefile);
  int save_errno = errno;
  free(buf);
  errno = save_errno;
  if (errno) return -1;
} while(0);  

使用块允许在需要重用时在各个位置存在本地save_errno。所有这一切似乎取决于free()是否设置errno。

Linux man页面不仅适用于malloc()等函数,还触及了malloc()设置errno的问题,但未提到free()是否会设置errno。

GNU C Library手册没有提到free()是否会设置errno。

因此,我编写了一个短程序来强制写入错误,以查看free()是否重置了errno,但它并没有重置。我在思考是否应该依赖这个结果和free()是如此关键,以至于 "当然它不会设置errno"。

# See if free() resets errno on a bad write
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char **argv)
{
  char * buf = malloc(256);
  snprintf(buf,256,"%s\n", "Hello, World!");

  FILE *badfile;

  badfile = fopen("/dev/null","r");

  fputs(buf, badfile);
  free(buf);
  printf("%d\n", errno);
  printf("%s\n", strerror(errno));
}

9
一份好的参考文献应该有所帮助。链接为:http://pubs.opengroup.org/onlinepubs/9699919799/functions/free.html - Some programmer dude
6
调用函数时,如果函数没有显式设置 errno 的值,包括那些没有失败的函数调用,errno 的值是未定义的。换句话说,在上一个调用显式失败之前,请不要检查 errno。因此需要注意。 - Some programmer dude
3
free的“滥用”,例如尝试两次释放同一指针,不一定会崩溃,它只是未定义的。虽然在许多情况下,未定义的行为会导致崩溃。 - Some programmer dude
1
http://pubs.opengroup.org/onlinepubs/9699919799/functions/errno.html#tag_16_110 - Werner Henze
4
你不必使用 do {...} while(0); 的结构,你也可以用简单的花括号 {...} 来创建代码块。 - Quentin
显示剩余2条评论
6个回答

52

POSIX并没有定义free设置errno的行为(尽管目前POSIX并未禁止,因此实现可能会这样做——请参考@ArjunShankar's answer了解更多细节)。但这与您的关注点无关。

您检查错误的方式不正确。您应该检查fputs的返回值,并检查它是否小于0。如果是,则可以检查errno以查找导致失败的原因,但这是可选的(并且应在调用任何进一步的函数之前进行)。

因此,像这样做就可以了:

int result = fputs(buf, somefile);
/* optionally read errno here if result < 0 (before the free call) */
free(buf);
return (result < 0) ? -1 : 0;

10
来自 errno 手册的相关引用:只有当函数调用返回错误时(即大多数系统调用返回 -1,大多数库函数返回 -1 或 NULL)它的值才有意义;成功的函数允许更改 errno 的值,因此在这种情况下它的值不具有任何意义。 - Mark
@Mark:有趣的事实:我发现execvp()在成功时会改变errno - Joshua

40

符合POSIX标准的free函数可能会在今天设置errno,但未来这将会变得更好。详情:

  1. The Open Group Base Specifications Issue 7中对errno的定义如下:

该POSIX.1-2008卷中的任何函数都不应将errno设置为0。对于成功调用函数后的errno设置未指定,除非该函数的描述指定不修改errno。

  1. free本身的定义没有说明free会如何处理errno
这意味着符合规范的free实现将永远不会将errno重置为0。但它可能会或可能不会将其设置为非零值。 然而,规范的第8版(正在进行中)要求free在传递有效输入时具体保证不设置errnoglibc已经准备遵守这个新要求。

在什么情况下,当传递有效输入时,free会设置errno? - Random832
@Random832 这取决于具体的实现方式。也许有些不会触及errno。请看我在回答中最后一行提供的链接,这是关于glibc实现中可能发生的情况的详细信息(需要明确的是,这并不是一个错误,因为POSIX尚未修订)。 - ArjunShankar
1
想象一下一个malloc/free实现,它尝试在可能的情况下缩小堆,使用适合底层操作系统的系统调用,例如mmap()、sbrk()等。如果这些系统调用失败,errno将被设置。 - Kyle Jones
@Random832,一个函数无条件地将errno设置为最常见的错误,然后使用返回值来指示该错误是否实际发生是完全有效的。 - cjm

17

在 C 标准中,free 的描述中没有提到 errno,因此您不应该依赖这个特性。

根据 C 标准(7.5 错误 <errno.h>)

  

3...无论是否有错误,库函数调用都可以将 errno 的值设置为非零,前提是在本标准的函数描述中未记录使用 errno 的情况。

正如我之前所说,在 C 标准的 free 描述中也没有记录使用 errno 的情况。


13

如果参考文献没有说明函数在失败时将返回错误代码到errno中,则不会返回。
几乎总是会以某种方式信号表示这些函数将errno设置为错误代码-内存分配函数返回NULL,许多其他函数返回零或负数等等。
如果它们成功,这些函数通常不需要以任何方式修改errno

通常情况下,您无法检查errno以确定是否发生了错误; 它仅用于在知道有错误发生后检索更多信息。

最终规则的一个例外是strto{l, d, ul}系列函数,但第一段同样适用于它们。
除非它们失败,否则它们也不一定设置errno,因此您需要首先清除它,否则它可能包含过期的错误代码。


2
这并不是真的。一个众所周知的例外是 strtol:如果你想检测某些错误条件,你需要在接收到有效值(LONG_MIN 或 LONG_MAX)之前清除 errno 并在接收到有效值后检查它。 - Random832
有趣。我添加了一些关于strto*的内容。 - molbdnilo

0

是的,free() 在某些系统上可能会破坏 errno。gnulib 通过在这些平台上替换 free() 来避免这个问题,并将其记录为: “glibc 2.32、Mac OS X、FreeBSD、NetBSD、OpenBSD 4.4、Minix、AIX、HP-UX、IRIX、Cygwin、mingw、MSVC”。


-3

你可以使用RAII来释放malloc分配的内存,并检查fputs的返回值。这将是优雅的代码。

//if malloc successfully
AutoFree af(buf);
if (fputs(buf, somefile)) {
LOG("something err:%s", strerror(errno));
}
return 0;

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