【问题标题】:POSIX fopen(NULL,"r")POSIX fopen(NULL,"r")
【发布时间】:2012-09-19 04:36:16
【问题描述】:

我想知道,使用 NULL 文件名调用 fopen 是否合法。如果是,它为我节省了 2 行代码。在我的 GNU/Linux 机器上它可以工作,但它必须是可移植的。我查看了 POSIX fopen,但它没有说明这个案例。是未定义的行为吗?

【问题讨论】:

  • 节省 2 行代码做什么?你希望它做什么?
  • 我有一个函数,如果它不能得到它,它可能会返回一些文件名,或者 null。我没有区别,文件名是否为空(无法获取),或者它指的是不存在或不可读的文件。
  • 你没有有多余的行,如果你想变得讨厌:FILE *f; if(!filename || !(f = fopen(filename, "r"))) { return NULL; }
  • 没关系。如果允许我将 NULL 传递给 fopen,它将使我的代码更清晰。我是吗?
  • 它不太可能是便携的。不要作恶。我在很多不同的 unix 上运行代码,我没有专门尝试过 fopen,但根据经验,当您将 NULL 传递给库函数时,总会有一个平台出现段错误。

标签: c io posix fopen


【解决方案1】:

正如Alexey points out,当NULL 参数的行为未指定时,这几乎可以肯定意味着行为未定义。但我想补充一点,因为您有正当的理由想要将它干净地滚动到一个表达式中。

你可以写一个函数:

FILE *
fopen_safe(char const *fname, char const *mode)
{
    if(fname == NULL)
        return NULL;

    return fopen(fname, mode);
}

如果您想避免函数调用的开销,也可以使用宏:

#define fopen_safe(fname, mode) \
    ( ((fname) == NULL) ? (NULL) : (fopen((fname), (mode))) )

【讨论】:

  • 在编写宏时,请务必注意将参数包含在括号中,以确保在所有情况下都能获得预期的行为。宏的一个不幸的缺点是没有简单的方法来防止不需要的副作用,例如如果你有一个宏和一个函数都命名为square,那么square(++x)(square)(++x) 会有不同的行为。前者(宏)会给你(x + 1) * (x + 2),后者(函数)会给你(x + 1)^2。当然,调用者端的明显解决方案是避免在宏调用中改变参数。
【解决方案2】:

来自 C 标准(从 1999 年开始):

7.1.4 库函数的使用

第 1 条:

Each of the following statements applies unless explicitly stated otherwise
in the detailed descriptions that follow: If an argument to a function has an
invalid value (such as a value outside the domain of the function, or a
pointer outside the address space of the program, or a null pointer, or a
pointer to non-modifiable storage when the corresponding parameter is not
const-qualified) or a type (after promotion) not expected by a function with
variable number of arguments, the behavior is undefined.

同一标准中对fopen()的描述根本没有提到NULLnull pointer

因此,根据 C 标准,将 NULL 作为文件名字符串指针传递给 fopen() 会导致未定义的行为。

但是,POSIX 可能会扩展该行为。

【讨论】:

    【解决方案3】:

    POSIX 似乎对这种情况有明确的含义

    [ENOENT]
    文件名的组件没有命名现有文件或文件名是空字符串。

    因此您可以返回指向(静态)"" 的指针,而不是空指针。

    【讨论】:

    • 它在其他地方会产生问题——我可以将 NULL 传递给 free,但不能传递静态字符串。顺便说一句,我使用 gnulib,但我不知道,它是否真的检查这种情况。
    • 如果需要,您也可以使用非静态字符串,但每次都必须分配它(malloc(1) 看起来有点傻!)。
    • 是的,它伤害了C程序员的直觉。
    猜你喜欢
    • 2012-01-14
    • 2015-03-10
    • 2021-12-02
    • 2015-02-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-08
    相关资源
    最近更新 更多