Printf变量字符串长度说明符

7

我有一个包含字符串和长度的结构体:

typedef struct string {
  char* data;
  size_t len;
} string_t;

这一切都很好,但是我想使用类似于printf的函数输出此结构体的内容。data可能没有空终止符(或在错误位置),因此我不能仅使用%s。但是,%.*s格式说明符需要一个int,而我有一个size_t

所以问题现在是,如何使用printf输出字符串?


7
只要size_t的值在int类型范围内,就可以将其转换int - Kerrek SB
3
如果长度超出了int的范围,那么这将是一个有趣的printf调用。应该检查一下,因为可能会溢出。 - cnicutar
6
如果“data”可能包含非可打印字符(例如空字符),则根本不应该使用“%s”。请编写一个循环。 - Carl Norum
3个回答

14
假设您的字符串中没有嵌入任何NUL字符,您可以在将转换为之后使用%.*s格式说明符:

假定您的字符串不包含任何嵌入的NUL字符,在将转换为之后,您可以使用%.*s格式说明符:

string_t *s = ...;
printf("The string is: %.*s\n", (int)s->len, s->data);

这也是假设您的字符串长度不超过 INT_MAX。如果您的字符串长度超过 INT_MAX,那么您会有其他问题(例如打印出 20 亿个字符需要相当长的时间)。


不太可能,但是原帖的作者可能针对一个带有16位int的嵌入式或历史设备。 - user4815162342
这真的是做这件事情的最佳方式吗?当我第一次注意到这种行为时,我的第一个问题是“为什么它不是size_t呢?” - Jeremy Rodi
1
@JeremyRodi:不确定,但我猜测它使用int而不是size_t是因为它要向后兼容在C语言标准化之前编写的代码,甚至在size_t类型出现之前。 - Adam Rosenfield

4

一个简单的解决方案就是使用未格式化输出:

fwrite(x.data, 1, x.len, stdout);
This is actually bad form, since `fwrite` may not write everything, so it should be used in a loop;
for (size_t i, remaining = x.len;
     remaining > 0 && (i = fwrite(x.data, 1, remaining, stdout)) > 0;
     remaining -= i) {
}

请确保x.len不大于SIZE_T_MAX。在使用fwrite时要注意这一点。

但是如果我想使用类似snprintf的东西,这样做就不太好了。虽然在那种情况下我可以只复制数据,但我更愿意能够使用其他格式说明符(如“%i”)。 - Jeremy Rodi
@drderp: 我不明白。你是怎么使用 snprintf 的?你可以使用 memcpy 来写入内存位置,而不是文件... - Kerrek SB
@KerrekSB 如果我想创建一个字符串和一个数字,我必须先使用memcpy复制字符串,然后使用snprintf在我想要的位置输出数字。我认为这不如一次性完成所有操作好... - Jeremy Rodi
@drderp:是的。这取决于您是否想处理带有空字节的任意数据。如果不想,使用%.*s格式化打印更可取。 - Kerrek SB
@JeremyRodi:不,打印会在第一个空字节处停止。 - Kerrek SB
显示剩余6条评论

1

如何使用printf输出字符串?

一次性输出? 由于您可能在奇怪的地方有空终止符号,因此无法以任何有意义的方式进行操作。 通常,如果您的缓冲区可能包含不可打印的字符,则需要在输出字符串时确定您希望如何打印(或不打印)这些字符。 编写循环,测试每个字符,并根据逻辑判断打印它(或不打印)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接