是否在snprintf中调用strlen会导致此段错误?

时间:2010-06-30 22:22:52

标签: c segmentation-fault strlen printf

我有一个void *,称之为data,我知道它的长度,但不会终止。我拨打了这样的电话snprintf(line, sizeof(line), "%*s", n, (const char*)data),其中n是已知的长度。几乎总是这样,但偶尔会导致段错误。

每当发生段错误时,后面跟踪表明问题出在strlen内部。当我在gdb中打印data时,我看到类似这样的内容

(gdb) p n
$1 = 88
(gdb) p (const char*) data
$2 = 0x1d752fa8
"JASDF" ... "ADS"<Address 0x1d753000 out of bounds>
(gdb) p 0x1d753000-0x1d752fa8
$3 = 88

data确实是88个字符,但不是空终止,事实上,它似乎正好对着一个段。我的猜测是snprintf在数据上总是被称为strlen,我通常很幸运,因为即使data没有空终止,在我点击该段之前还有一个\0然后我偶尔会感到不幸它是。是对的吗?如果是这样,那有什么作用?

这就是堆栈跟踪的样子

#0  0x0000003c8927839e in strlen () from /lib64/libc.so.6
#1  0x0000003c89246749 in vfprintf () from /lib64/libc.so.6
#2  0x0000003c8926941a in vsnprintf () from /lib64/libc.so.6
#3  0x0000003c8924d0a3 in snprintf () from /lib64/libc.so.6

编辑要回答我自己关于解决方法的问题,strncpy是一个更适合调用的函数。我习惯使用snprintf。

4 个答案:

答案 0 :(得分:7)

  

snprintf(line, sizeof(line), "%*s", n, (const char*)data)

data不是零终止?那你做错了。

snprintf(line, sizeof(line), "%.*s", n, (const char*)data);

注意点。

如果是字符串,*中的第一个*.*如果是所需的输出(屏幕上)长度 - 输入长度是第二个*man printf了解更多信息。

显然在%*s格式化的情况下可能会调用strlen(),因为它需要知道是否需要填充输出以及如何填充它。

答案 1 :(得分:6)

看起来你是对的。我无法保证printf不会调用strlen,即使它不一定要在给定的上下文中。你是通过提供一个非C字符串作为%s格式说明符的参数而撒谎,所以你违反了printf的契约。未定义的行为结果。

答案 2 :(得分:1)

我认为你的分析是正确的。如果缓冲区未终止null,则strlen调用将一直读取,直到找到\0。如果它超出了段的末尾(并且下一段无效),那么它将产生异常。

解决方案是将其终止或将其放入另一个可以null终止的缓冲区中。

答案 3 :(得分:0)

“解决方法”是使用memcpy()

size_t validlen = n < sizeof line ? n : (sizeof line - 1);
memcpy(line, data, validlen);
line[validlen] = '\0';