由 tmpnam 创建的文件名的 fopen 在 mingw 上失败

Posted

技术标签:

【中文标题】由 tmpnam 创建的文件名的 fopen 在 mingw 上失败【英文标题】:fopen of file name created by tmpnam fails on mingw 【发布时间】:2016-08-10 09:07:42 【问题描述】:

我有一个程序通过tmpname 生成一个临时文件名,然后尝试通过fopen 打开该文件。但是,当使用 MinGW 构建程序时,这始终失败,但在使用 VS2015 构建时始终在同一台机器上工作。这是一个简单的测试:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char** argv)

    char fname[L_tmpnam];
    if(!tmpnam(fname)) return EXIT_FAILURE;
    printf("%s\n", fname);
    FILE * f = fopen(fname, "wb");
    if(!f) return EXIT_FAILURE;
    fclose(f);
    remove(fname);
    return EXIT_SUCCESS;

通过 gcc 构建和运行

gcc test.c && a && echo ok || echo failed

输出

\s4so.
failed

通过 VS2015 构建和运行

cl test.c && test && echo ok || echo failed

生产

%TMP%\s11w.0
ok

其中%TMP% 是 TMP 环境变量的值。

失败的原因很明显(MinGW 没有添加 TMP 文件夹,而是尝试写入用户没有写入权限的当前驱动器的根目录)。然而,为什么会发生这种情况?我错过了一个电话,还是这是一个错误?

【问题讨论】:

也许this answer 有帮助 你可以使用perrorif(!tmpnam(fname)) perror("tmpnam"); exit(EXIT_FAILURE); @AlterMann 我认为tmpnam 成功,因为检查了返回值。问题是返回的文件名指向root,不允许程序在那里写.. @LPs 不是真的。在 windows 中以斜杠开头的文件名是相对于当前分区根目录的,而不是相对于当前文件夹的。投票最多的评论和投票最多的答案似乎都不理解该问题。 MSVC 和 MinGW 之间的行为也明显不同,所以我不能简单地在前面加上一个点或其他东西。 @AlterMann tmpnam 失败已通过返回码报告。而且它不会失败。 【参考方案1】:

但是,为什么会发生这种情况?我错过了一个电话,还是这是一个错误?

标准没有指定文件的位置。所以这不是错误,它是tmpnam 的两种不同实现的示例

BTW:在 Visual C++ 2015 之前,Visual C++ 也使用驱动器的根目录。见https://msdn.microsoft.com/en-us/library/bb531344.aspx

【讨论】:

标准没有指定它应该放在哪里并不意味着一个从不返回可用路径的实现应该被认为是“好的”。但很高兴知道 MSVC 曾经有同样的错误。可能只是MinGW盲目使用msvcrt.dll的神器(技术上根本不应该使用)【参考方案2】:

看着MSDN

如果定义了 TMP 环境变量并将其设置为有效的目录名,则会为 TMP 指定的目录生成唯一的文件名。

如果未定义 TMP 环境变量或将其设置为不存在的目录名称,_tempnam 将使用 dir 参数作为生成唯一名称的路径。

如果未定义 TMP 环境变量或将其设置为不存在的目录名称,并且如果 dir 为 NULL 或设置为不存在的目录名称,@ 987654325@ 将使用当前工作目录生成唯一名称。目前,如果 TMP 和 dir 都指定了不存在的目录名称,_tempnam 函数调用将失败。

强调我的

似乎在 Windows 上重新定义了该行为。

这不是我 man 定义的 linux 行为

创建的路径名具有目录前缀P_tmpdir。 (两个都 L_tmpnam 和 P_tmpdir 在&lt;stdio.h&gt; 中定义,就像TMP_MAX 下面提到。)

强调我的

在我的系统上,P_tmpdir 设置为 "/tmp",代码运行良好,返回 /tmp/something

另外查看stdio.h可以发现P_tmpdir的声明有条件:

#if defined __USE_SVID || defined __USE_XOPEN
/* Default path prefix for `tempnam' and `tmpnam'.  */
# define P_tmpdir   "/tmp"
#endif

注意这些设置:在我的 features.h 文件中,__USE_SVID 定义为 1

简单来说:你必须照顾好平台,因为 windows 的行为与 linux 不同

【讨论】:

更改_P_tmpdir 的定义不会神奇地改变tmpnam 返回的内容。对于 MinGW,唯一影响 _P_tmpdir POSIX 的标志在 "/""\\" 之间进行选择。此外,/tmp 在 Windows 上同样是错误的。 @Joe 对不起,我没听懂你。 更改必须在 CRT 编译时完成。 整个P_tmpdir也是POSIX的东西,和C标准一点关系都没有,只规定tmpnam"[...] 生成的字符串是一个有效的文件名称,并且与现有文件的名称不同。” 你看链接的linuxman了吗?它不是“POSIX 事物”,是符合 C89、C99 的。此外gcc-std=c11 wanrs:warning: the use of tmpnam' 是危险的,最好使用mkstemp'

以上是关于由 tmpnam 创建的文件名的 fopen 在 mingw 上失败的主要内容,如果未能解决你的问题,请参考以下文章

fopen(),fclose() 打开/关闭文件

fopen

fopen创建多级目录下文件的问题

fopen 创建文件,但是如何更改权限?

fopen() 创建权限模式

PHP 文件创建/写入