【问题标题】:Characters written so far in snprintf到目前为止在 snprintf 中编写的字符
【发布时间】:2014-10-22 17:56:04
【问题描述】:

最近,我注意到一个奇怪的案例,我想验证一下:

对于 SUS,对于格式字符串中的 %n,相应的 int 将设置为写入到输出的字节数。 此外,对于snprintf(dest, 3, "abcd")dest 将指向"ab\0"。为什么?因为要写入输出(dest 缓冲区)的字节数不超过 n (n = 3)。

我为代码推断了这一点:

int written;
char dest[3];
snprintf(dest, 3, "abcde%n", &written);

written 将设置为 2(从计数中排除空终止)。 但是根据我使用 GCC 4.8.1 进行的测试,written 设置为 5。 我是否误解了标准?它是一个错误吗?是未定义的行为吗?

编辑:

@wildplasser 说:

...格式字符串中 %n 的行为可能未定义或实现已定义...

...实现必须模拟处理完整的格式字符串(包括 %n)...

@par 说:

written 是 5,因为这是在遇到 %n 时将写入的字符数。这是正确的行为。 snprintf 最多只能复制 size 字符减去尾随的 null ...

还有:

另一种看待这个问题的方式是,如果 %n 只处理最多 2 个字符,甚至不会遇到它,因此可以想象 written 的值无效......

还有:

...整个字符串通过printf() 规则处理,然后应用最大长度...

是否可以验证为标准、标准草案或某些官方来源?

【问题讨论】:

  • 对我来说看起来像一个错误(在库实现中)。
  • 我希望您应该首先查看 snprintf() 的返回值。 “副作用”是不同的;如果返回值 >= 第二个参数,则格式字符串中 %n 的行为可能未定义或实现已定义。在任何情况下,实现都必须模拟处理完整格式字符串(包括%n)以获得正确的返回值,因此可以获得副作用在飞行中。

标签: c gcc c99 format-string


【解决方案1】:

written 是 5,因为这是在遇到 %n 时将写入的字符数。这是正确的行为。 snprintf 仅复制最多 size 字符减去尾随空值(因此在您的情况下为 3-1 == 2。您必须将字符串格式化行为与仅写入这么多字符分开。

另一种看待这个问题的方法是,如果 %n 只处理最多 2 个字符,甚至不会遇到它,因此可以预期 written 具有无效值。如果您在遇到 %n 时期望在 written 中存在有效的内容(但实际上没有),那么这就是存在错误的地方。

所以请记住,整个字符串是通过printf() 规则处理的,然后应用最大长度。

【讨论】:

  • 嗯,标准的文本包含 "... 到目前为止写入输出流的字符数..."; 'c''d''e' 未写入输出流
  • @pmg - 字符写入输出流,只要输出流是 printf() 函数中“幕后”的数据缓冲区。 然后 发生仅从“输出流”中复制最多 size 个字符的 snprintf 行为。有关 ISOC99 的参考信息,请参阅 Heinz 的回答。
  • 再一次,可能引用"%n" 的文本不适用于sprintf(),因为此函数不会写入输出流。
  • 想想你将如何实现snprintf():你会调用vasprintf(),然后是memcpy(),然后是free()。这里可以清楚的看到输出流是vasprintf()分配的缓冲区,但它发生在幕后。
  • @par 也许你会这样做......关心堆栈溢出的实现者不会
【解决方案2】:

这不是错误:ISOC99 说

snprintf 函数等价于 fprintf [...] 第 n-1 个之后的输出字符被丢弃而不是写入数组 [...]

所以它只是丢弃尾随输出,但在其他方面表现相同。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多