如何避免在 C 中使用非空终止的缓冲区调用 fopen()?

How to avoid calling fopen() with a buffer that is not null-terminated in C?

我们来看这个例子:

static FILE *open_file(const char *file_path)
{
    char buf[80];
    size_t n = snprintf(buf, sizeof (buf), "%s", file_path);
    assert(n < sizeof (buf));
    return fopen(buf, "r");
}

在这里,assert() 差一。来自 snprintf 的联机帮助页:

"Upon successful return, these functions return the number of characters printed (excluding the null byte used to end output to strings)."

因此,如果它 returns 80,则字符串将填充缓冲区,并且不会被 \0 终止。这将导致问题,因为 fopen() 假定它是空终止的。

防止这种情况的最佳方法是什么?

What is the best way to prevent this?

很简单,不要给它一个非空终止字符串。撇开学术问题不谈,您可以控制自己编写的代码。您不必以各种可能的方式防止自己破坏项目,您只需要不破坏自己即可。

如果每个人都检查并仔细检查代码中的所有内容,性能损失将是难以置信的。 fopen 不这样做是有原因的。

So, if it returns 80, then the string will fill the buffer, and won't be terminated by [=11=]

这是不正确的:无论您为 file_path 传递什么,字符串都将以 null 结尾。显然,字符串会在 sizeof(buf)-1.

处被截断

请注意 snprintf 也可以 return 大于 80 的数字。这意味着您要打印的字符串比您提供的缓冲区长。

What is the best way to prevent this?

您已经这样做了:assert 不是防止未终止字符串所必需的。您可以使用 return 值来决定是否发生任何截断,并传递一个更大的缓冲区来补偿:

// Figure out the size
size_t n = snprintf(NULL, 0, "%s", file_path);
// Allocate the buffer and print into it
char *tmpBuf = malloc(n+1);
snprintf(tmpBuf, n+1, "%s", file_path);
// Prepare the file to return
FILE *res = fopen(tmpBuf, "r");
// Free the temporary buffer
free(tmpBuf);
return res;

这里有几个问题。

首先 assert() 用于捕获问题作为设计器测试的一部分。它不适用于生产代码。

其次,如果文件路径不完整那么你真的要调用fopen()吗?

通常所做的是将预期的字符数加一。

static FILE *open_file(const char *file_path)
{
    char buf[80 + 1] = {0};

    size_t n = snprintf(buf, 80, "%s", file_path);
    assert(n < sizeof (buf));
    return fopen(buf, "r");
}