【问题标题】:Need to clean up errno before calling function then checking errno?需要在调用函数之前清理errno然后检查errno吗?
【发布时间】:2025-12-08 10:10:01
【问题描述】:
  1. 在调用函数之前我们需要将errno 重置为零吗?请参见下面的代码。现在的场景是a_dest_path 是一个现有目录。但是当我执行代码时,它总是尝试mkdir 但返回错误表示无法创建目录,因为它存在。在 GDB 中,我在调用opendir() 之前检查了errno,而errno 是2。在调用opendir() 期间,errno 似乎没有设置为零。那么在调用opendir() 之前是否需要将errno 重置为零?

  2. errno 可以在system() 调用中更改,然后在我的else if 分支中,我检查来自system() 但不是opendir() 的结果。那么在opendir()之后,我是否需要将errno分配给一个变量,然后在if..elseif..else分支中检查这个变量?

DIR *dp = opendir(a_dest_path.c_str());
if (errno == ENOENT) {
    string mkdir_comman = "mkdir " + a_dest_path;
    system(mkdir_command.c_str());
} else if (errno == ENOTDIR) {
    printf("Destination %s exists but is not directory\n", a_dest_path.c_str());
    return k_error_not_directory;
} else if (errno == 0) {
    closedir(dp);
}

【问题讨论】:

  • 对于 2。我认为您误解了 if() {} else if () {} 的工作原理。如果第一个 if 条件为真,则不会测试 else if 条件
  • 清除errno 仅在您调用的函数不表示其返回值存在错误时才需要(例如,返回类型为void 的函数和返回的函数值的域对于正常和非错误值是完全有意义的)。

标签: c++ linux


【解决方案1】:

在您的示例中,正如其他人所说,无需重置errno

然而,有极少数函数会返回一个数字,对于它们来说,没有一个数字可以 100% 表示发生了错误。在这少数情况下,重置errno 是必要的。

这种函数的一个例子是strtol()。出错时返回-1。如果你真的想知道用户是否传递了"-1"或者是否发生了错误,那么你想做:

errno = 0;
long r = strtol(input, &end, 10);
if(r == -1 && errno != 0)
    ...handle the error...

在大多数情况下,此类函数确实会在其 DESCRIPTIONRETURN VALUE 手册部分中说明问题。

正如其他人所提到的,一个返回指针的函数,当它返回一个nullptr时它表示一个错误,那么你不需要重置errno,因为你应该只在函数返回nullptr时检查那个变量。

ptr = malloc(123);
if(ptr == nullptr)
   ...handle error, 'errno' may give you extra hints about error...

【讨论】:

    【解决方案2】:

    不,调用函数之前不需要重置errno,因为the contract reads

    opendir()fdopendir() 函数返回一个指向目录流的指针。出错时返回NULL,并适当设置errno

    测试返回值,只有在实际出错时才查看errno

    realloc 是一个罕见的函数示例,在特定情况下,如果不查看 errno,就无法将错误与成功区分开来,但如果需要消除歧义,它会清除它。)

    【讨论】:

    • 谢谢。因此,如果在函数调用期间没有发生错误,则 errno 不会设置为零,对吗?在 if.else 分支中检查 errno 是否不安全,因为如果在第一个 if 分支中发生错误,则 errno 可能会设置为另一个值,例如我的代码中的 system() 函数?谢谢。
    • 除了记录在案的一些特定例外,errno 仅在发生错误时更新。
    • 您的转录并没有说明当这些函数开始工作时 errno 将被重置。那么,你有什么安全措施没有在之前的指令中设置 errno?
    • @sergiol 正如我所说,先测试是否发生错误。
    • 所以 errno 不会告诉你发生了什么错误,它只会告诉你最后发生了什么错误。将“发生的事情”与 errno 两者都做的“发生的事情”结合起来会更有效率。就目前而言,errno 具有误导性,因为它会挂在最后发生的错误上,并且谁知道发生的位置,除非您在每次调用后将其重置,否则在发生错误时可能将其设置为不同的值。
    【解决方案3】:

    errno 仅在发生真正错误后才有意义。您必须先检查错误情况,然后查看errno,例如:

    DIR *dp = opendir(a_dest_path.c_str());
    if (dp) {
        closedir(dp);
    } else {
        if (errno == ENOENT) {
            string mkdir_comman = "mkdir " + a_dest_path;
            system(mkdir_command.c_str());
        } else if (errno == ENOTDIR) {
            printf("Destination %s exists but is not directory\n", a_dest_path.c_str());
            return k_error_not_directory;
        } else {
            // some other error...
        }
    }
    

    【讨论】:

    • 这也不安全,因为 errno 可能在 if..else if 结构中被改变。如果system(mkdir_command.c_str())发生错误怎么办,然后errno改成system()的错误码而不是opendir()
    • 如果errno 发生变化怎么办?您应该在通话失败后立即检查errno,切勿拖延检查。如果system() 失败,请再次查看errno 找出原因并采取相应措施。
    • 是的,errno 可以通过 system() 更改。一种解决方案是将 errno 分配给一个变量,然后检查这个变量,这可以确保我们正在检查 opendir() 的错误。目录 *dp = opendir(a_dest_path.c_str());如果 (dp) { 关闭 (dp); } 其他 { int err_opendir = errno; //然后检查下面代码中的err_opendir if (errno == ENOENT) {
    • 但是在调用system() 时,您已经知道errno 的先前值,它是ENOENT,所以谁在乎它是否改变?对system() 的调用下方的行上的else if 没有经过测试,因为第一个if 是真的
    • @WilliamCerniuk errno 存储在每个线程的基础上,因此线程不是问题。一个线程不能影响另一个线程的errno。见Is errno thread-safe?