【发布时间】:2016-06-13 11:12:49
【问题描述】:
我正在将一些非常古老(> 10 年)的 C 代码移植到现代 Linux 上。我在自定义编写的 vsnprintf() 包装器中遇到分段错误(显然它的任务是检测重复的输出字符串并实习它们):
char* strVPrintf(const String fmt, va_list ap)
{
/* Guess we need no more than 50 bytes. */
int n, size = 50;
char* p = (char*)memMalloc(size), q;
while (1) {
/* Try to print in the allocated space. */
n = vsnprintf(p, size, fmt, ap);
/* If that worked, return the string. */
if (n > -1 && n < size) {
break;
}
/* Else try again with more space. */
if (n > -1) /* glibc 2.1 */
size = n + 1; /* precisely what is needed */
else /* glibc 2.0 */
size *= 2; /* twice the old size */
p = memRealloc(p, size);
}
q = strRegister(p);
memFree(p);
return q;
}
作者似乎假设标准vsnprintf() 函数返回写入的字符数,如果它没有接收到足够的空间来格式化所有args,则简单地返回一个标记值。这意味着您可以猜测缓冲区大小并在必要时增加它。
但是在我的系统(Ubuntu 14.04,glibc 2.19)上,当使用太多参数调用提供的空间时,vnprintf 会导致分段错误。与此同时,snprintf() 家族的语义是否发生了巨大变化?确保您提供足够缓冲空间的现代方法是什么?
【问题讨论】:
-
man vsnprintf: 函数 snprintf() 和 vsnprintf() 不会写入超过 size 字节(包括终止空字节 ('\0'))。如果输出由于此限制而被截断,则返回值是字符数(不包括终止的空字节),如果有足够的空间可用,这些字符将被写入最终字符串。因此,大小或更大的返回值意味着输出被截断。 程序的那部分似乎是正确的。 -
你没有检查内存分配失败。
-
什么是
String?我怀疑是char *,但您也应该提供该信息。 -
@AndrewHenle 你是对的,解决了这个问题。
-
附带问题:1) 我希望
size_t size而不是int。 2) IMOn >= 0比n > -1更容易关注。