4

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

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

(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 个字符,但不是以 null 结尾的,事实上,它似乎正好靠在一个段上。我的猜测是 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

4 回答 4

10

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

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

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

注意点。

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

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

于 2010-06-30T23:17:28.157 回答
6

看起来你是对的。不能保证printf不会调用strlen,即使在给定的上下文中不一定必须调用。您通过提供不是 C 字符串的内容作为%s格式说明符的参数是在撒谎,因此您违反了printf. 未定义的行为结果。

于 2010-06-30T22:30:37.537 回答
1

我认为你的分析是正确的。如果缓冲区不是以空值终止的,则 strlen 调用将读取,直到找到一个\0. 如果它运行超过段的末尾(并且下一个段无效),那么它将产生异常。

解决方案是空终止它或将它放在另一个可以空终止的缓冲区中。

于 2010-06-30T22:31:46.473 回答
0

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

size_t validlen = n < sizeof line ? n : (sizeof line - 1);
memcpy(line, data, validlen);
line[validlen] = '\0';
于 2010-06-30T22:40:56.153 回答