fopen因GetLastError()= 0而失败 - 为什么会这样?

时间:2014-10-02 10:23:56

标签: c++ fopen

我有一个加载的环境,有很多线程打开同一个文件进行读取。 问题是 - 当我fopen时我得到null,但是errno是0。

这怎么可能?

代码:

 if ((fParm = fopen(FullFileName.c_str(), "r")) == NULL)
  {
      printf("%d", GetLastError());
  }

正如MSDN中指出的那样 - 在fopen失败后 - 你应该检查errno。这就是我们的工作:

http://msdn.microsoft.com/en-us/library/yeby3zcb.aspx

(忽略printf,原始代码中没有使用它 - 我只是为了简化问题而写它。)

感谢您的帮助。

----------------------编辑------------------------ -

我实际上使用了GetLastError()而不是好的ol' errno - 导致错误显示为0。

5 个答案:

答案 0 :(得分:2)

C标准不保证errno将被设置为有意义的值,尽管POSIX定义它将在失败的情况下返回NULL并设置errno,IEEE Std 1003.1-2013

因此,根据链接库中使用的实现和已实现的标准集,您可能会看到不同的结果。

您在评论中提到您在Windows上使用GetLastError(),应该是thread-safe。遗憾的是,您没有编辑主要问题以反映散布在评论中的所有这些事实。

答案 1 :(得分:2)

如果这是Windows并且您使用的是GetLastError,则您的代码将无效。文档很清楚:

  

这些函数[fopen_wfopen]中的每一个都返回指向打开文件的指针。空指针值表示错误。如果filename或mode为NULL或空字符串,则这些函数会触发无效参数处理程序,参数验证中对此进行了描述。如果允许继续执行,则这些函数返回NULL 并将errno 设置为EINVAL。 - MSDN(强调补充)

与其他平台一样,fopen通过设置errno返回错误。您不应该调用GetLastError,而是记录errno的值。它们是两个完全不同的东西。

在某些情况下,调用GetLastError可能会发生意外。如果错误的根本原因是来自Windows API中的另一个函数的错误(如CreateFile),那么可能会调用SetLastError那个其他函数,并且很可能{{1}中没有其他代码将改变该值,因此有时可能会发生正确的错误。但如果错误来自fopen本身,fopen将无法提供正确的信息。

答案 2 :(得分:0)

我们必须猜测,因为您没有向我们展示您的真实代码。但有一种可能性:你的真实代码是这样的:

 if ((fParm = fopen(FullFileName.c_str(), "r")) == NULL)
 {
      SomeLogMacro(Severity, "%d", errno);
 }

那个宏扩展到这样的东西:

 if ((fParm = fopen(FullFileName.c_str(), "r")) == NULL)
 {
      do { if (ShouldLog(Severity)) DoLog("%d", errno); } while (false);
 }

如果ShouldLog函数更改了errno的值,则会记录错误的值。

答案 3 :(得分:0)

阅读MSDN documentation for fopen我发现它没有设置lasterror(它只是在失败时返回0)。阅读MSDN documentation for GetLastError我看到它返回的是,任何人(不是fopen)设置的最后一个错误。

答案 4 :(得分:0)

编辑David Schwartz是对的:问题是fopen没有打电话给GetLastError - 见下文

好的,我在评论中看到你有#define GetLastError() errno的其他答案。您应该在初始问题中声明,因为Posix需要errno是线程安全的,但我不确定它是否在Windows上,当我在MSDN中看到关于全局变量的警告(包括errno):

  

对于更安全的功能版本,已弃用这些全局变量,应使用这些版本代替全局变量

但是GetLastError 是线程安全的,只是微软说:

  

当函数的返回值指示此类调用将返回有用数据时,应立即调用GetLastError函数。这是因为有些函数在成功时调用SetLastError为零,消除了最近失败函数设置的错误代码。

所以唯一万无一失的方法是拥有一个本地变量,比如说lastError并执行:

if ((fParm = fopen(FullFileName.c_str(), "r")) == NULL)
  {
      int lastError = errno; // in reality calls GetLastError immediately and saves it current value even if other functions later calls SetLastError(0)
      printf("%d", lastError);
  }

编辑:

事实上,fopen并未单独调用SetLastError。以下是该问题的演示:

#include <stdio.h>
#include <windows.h>
#include <errno.h>

int main() {
    FILE *fd = fopen("", "r");
    int lastErr = GetLastError();
    int err = errno;

    printf("errno : %d - GetLastError %d\n", err, lastErr);
    return 0;
}

结果是:

errno : 22 - GetLastError 0

实际上应该归咎于#define GetLastError() errno。但在多线程应用程序中,我真的建议您立即将errno复制到本地变量中。