用于字符串连接的snprintf

时间:2012-08-22 01:39:37

标签: c string

我正在使用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"。正确?

我做错了什么?

4 个答案:

答案 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的返回值来增加指针。