如何避免使用在C中不是以null结尾的缓冲区调用fopen()?
让我们看看这个例子:如何避免使用在C中不是以null结尾的缓冲区调用fopen()?,c,string,null,fopen,stdio,C,String,Null,Fopen,Stdio,让我们看看这个例子: 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"); } 静态文件*打开文件(常量字符*文件路径) { char-buf[80]; size\u t n=
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");
}
静态文件*打开文件(常量字符*文件路径)
{
char-buf[80];
size\u t n=snprintf(buf,sizeof(buf),“%s”,文件路径);
断言(n
在这里,assert()
按1关闭。从snprintf
的手册页:
成功返回后,这些函数返回打印的字符数(不包括用于结束字符串输出的空字节)
因此,如果返回80,则字符串将填充缓冲区,并且不会被\0终止。这将导致问题,因为fopen()
假定它以null结尾
预防这种情况的最佳方法是什么
预防这种情况的最佳方法是什么
很简单,不要给它一个以非null结尾的字符串。撇开学术问题不谈,您可以控制自己编写的代码。你不必保护自己不以任何可能的方式破坏项目,你只需要不破坏自己
如果每个人都检查并重复检查代码中的所有内容,那么性能损失将是难以置信的。fopen
不这样做是有原因的
因此,如果返回80,则字符串将填充缓冲区,并且不会被\0
这是不正确的:无论您为文件\u路径传递什么,字符串都将以null结尾。显然,字符串将在sizeof(buf)-1
处被截断
请注意,snprintf
也可以返回80以上的数字。这意味着您要打印的字符串比您提供的缓冲区长
预防这种情况的最佳方法是什么
您已经在这样做了:assert
对于防止未终止的字符串不是必需的。您可以使用返回值来确定是否发生了任何截断,并传递更大的缓冲区进行补偿:
// 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");
}
静态文件*打开文件(常量字符*文件路径)
{
char buf[80+1]={0};
大小n=snprintf(buf,80,“%s”,文件路径);
断言(n
断言(n<(sizeof(buf)-1))代码>?我看不出您的问题,如果它返回80个断言触发器,因为80<80
为false。问题中的语句“Theassert()
为off by 1”似乎为false。如果文件路径
的长度小于sizeof buf
,则其所有字符将成功写入buf
,并带有一个终止的空字节,代码将按需要工作。如果文件路径
的长度大于或等于sizeof buf
,则不会将其所有字符写入buf
,因为snprintf
必须提前停止,以便为终止的空字节留出空间。在这种情况下,assert
正确地捕捉到问题。它的测试似乎完全正确,没有一个错误。此代码在将文件路径
复制到buf
中时似乎没有任何作用。函数的整个主体可以替换为return fopen(文件路径,“r”)snprintf
与%s
一起使用,而是将snprintf
与更复杂的格式字符串一起使用,例如snprintf(buf,sizeof buf,“%s/%s.”,开始目录,文件路径,扩展名)代码>,以给出一个假设的示例。如果是这样,您应该显示实际的代码,或者至少显示更接近它的示例代码,以便提供更合适的建议snprintf
写入终止的空字节,因此无需使用空字节初始化buf
。和charbuf[80+1]={0}“代码>初始化它的全部,这是完全不必要的。@EricPostpischil如果您指出代码表明对snprintf()
函数的理解不完整,那么我同意您的看法。”。说这是浪费是一种道德判断,这确实是旁观者的看法。我认为,与打开一个文件所带来的性能损失相比,额外的一个字符和将char
数组初始化为零所造成的任何浪费都相形见绌。这不是道德判断,只是可笑的迂腐。。。初始化buf
比不初始化要好得多。这一点也不低效。如果buf
是buf[800000]
,那么注释可能更有意义,但是buf[80]
??真的吗?OP的问题和代码没有任何意义。您的新代码向他们展示了如何将任意长度的字符串复制到临时缓冲区中,但这有什么意义呢?代码返回fopen(文件路径,“r”)
将更好地完成相同的结果。@EricPostChil这绝对正确。我在这里的假设是OP的真实代码使用更复杂的格式字符串,需要打印更多的数据。否则,使用snprintf
完成整个练习是没有意义的。如果是这种假设,那么我们应该向OP展示如何使用snprintf(NULL,0,…)
,然后是malloc
,然后是snprintf(缓冲区,…)
,依此类推。