我正在使用snprintf将字符串连接到char数组:
char buf[20] = "";
snprintf(buf, sizeof buf, "%s%s", buf, "foo");
printf("%s\n", buf);
snprintf(buf, sizeof buf, "%s%s", buf, " bar");
printf("%s\n", buf);
问题是buf
的第二次连接,而不是添加"bar"
,用它替换"foo"
。输出如下:
foo
bar
第一个%s
应该保留buf
(在这种情况下保留"foo"
)。第二个%s
应附加"bar"
。正确?
我做错了什么?
答案 0 :(得分:28)
您违反了restrict
上的snprintf
合同,该合同规定没有其他参数可以与缓冲区重叠。
无论如何,将输入复制到自身都是浪费精力。 snprintf
返回格式化所需的字符数,因此请利用此功能进行追加:
char buf[20] = "";
char *cur = buf, * const end = buf + sizeof buf;
cur += snprintf(cur, end-cur, "%s", "foo");
printf("%s\n", buf);
if (cur < end) {
cur += snprintf(cur, end-cur, "%s", " bar");
}
printf("%s\n", buf);
答案 1 :(得分:3)
虽然接受的答案是正确的,但更好的(在我看来)答案是连接字符串是错误的。您应该在对snprintf
的单调用中构造整个输出。这是使用格式化输出函数的重点,它比指针算法和多次调用更有效,更安全。例如:
snprintf(buf, sizeof buf, "%s%s%s", str_a, str_b, str_c);
答案 2 :(得分:2)
试试这个:
char buf[20];
snprintf(buf, sizeof buf, "%s", "foo");
printf("%s\n", buf);
int len = strlen(buf);
snprintf(buf+len, (sizeof buf) - len, "%s", " bar");
printf("%s\n", buf);
输出是“foo bar”。 snprintf的第一个参数是一个指向char的指针,它将开始填充字符。它不关注缓冲区中的内容。 strlen函数确实很注意。它计算snprintf放在那里的nul(0)之前的字符数。因此,不要传递buf,而是传递buf + strlen(buf)。你也可以使用strncat,效率稍高。
我在你的问题下看到了C ++标签。查找std :: string。方式更好。
答案 3 :(得分:1)
为什么不使用strncat()
?它的目的是做到这一点:
char buf[20] = "";
strncat(buf, "foo", sizeof buf);
printf("%s\n", buf);
strncat(buf, " bar", sizeof buf - strlen(buf));
printf("%s\n", buf);
如果您的系统支持它,您可以使用strncat_s()
而不是strncat
,因为它具有额外的溢出保护级别,并且无需计算输出缓冲区中剩余的字节数。 / p>
如果必须使用snprintf
,则需要创建一个单独的指针来跟踪字符串的结尾。该指针将是您传递给snprintf
的第一个参数。您当前的代码始终使用buf
,这意味着它将始终打印到该数组的开头。您可以使用strlen
在每次snprintf
调用后查找字符串的结尾,也可以使用snprintf
的返回值来增加指针。