【问题标题】:What does snprintf return if it writes nothing when maxlen > 0?如果当 maxlen > 0 时 snprintf 什么也不写,它会返回什么?
【发布时间】:2013-10-05 18:17:01
【问题描述】:

根据我对 C 标准的阅读,如果 snprinf 在写入时达到 maxlen,它将返回一个正值或负值(实际上并不清楚),其大小等于失败的字符数要写。规范还说,该函数将为任何错误返回负值。所以我的问题是:在以下两种情况下它会返回什么......

Maxlen > 0,在发生错误之前没有写入任何字符,snprintf 保释

Maxlen > 0,所有字符都已写入,出现某种错误,snprintf 保释

我意识到,尤其是最后一个,不太可能。但我想更好地理解规范。在我看来,snprintf 的返回使您是否正确编写了字符串的一部分变得模棱两可。

        • 编辑

显然这并不清楚。让我详细说明:

*> 哪个负数无关紧要。错误就是错误,它没有

在发生之前写入了多少字节。 这可能是大多数人的实际情况。但是,我看到 snprintf 应该返回多余字符的数量,我想知道是否可以使用该值。 我觉得很奇怪,规范会遇到麻烦报告一些有用的东西,只是让它模棱两可,它是否真的返回了一些不相关的东西。这没有意义吗? 我想知道约定是否有更多内容可以确定 snprintf 是否因为输入过多而失败。*

我现在明白了。我看到除截断以外的任何错误都是负数,成功是 0

【问题讨论】:

  • "如果输出由于此限制而被截断,则返回值是字符数(不包括终止的空字节),如果有足够的可用空间,它将被写入最终字符串。 "这有什么歧义?
  • 你没有阅读我问题中间的两行。
  • 我做到了,但我仍然不确定你为什么感到困惑。
  • 历史:在 snprintf() 和朋友被 C89/C90 标准化之前,(not-yet standard) 函数的返回值有两种约定。一个总是返回-1,另一个使用大于第二个参数的值来给调用者一个提示。最后一个被标准采用。 (但您仍然必须检查返回值
  • 1.规范表明截断是一种错误,但是关于 glibcn 版本的评论表明截断返回正值并且不表示错误。更重要的是,它从未明确表示截断是一个错误。 2.无论标准是否模糊,返回值中的信息似乎都是。

标签: c printf


【解决方案1】:

这不是我阅读标准的方式。来自 n1570 草案(我认为是最终免费的):

snprintf 函数返回字符数 写的 n 足够大,不算 终止空字符,如果编码错误则为负值 发生了。因此,空终止输出已被完全写入 当且仅当返回值为非负且小于 n。

他们的原型是:

int sprintf(char * restrict s, const char * restrict format, ...);

...所以他们的“n”大概是你的“Maxlen”值。

所以,没有问题。如果出现错误,您会得到 -42 或其他任意负数。如果没有,您将获得要写入的所有数据的长度。适合的部分被写入缓冲区,如果返回值大于或等于n,则被截断。

我看到的唯一未指定的位是,由于nsize_t,因此结果可能大于正整数返回值。不过,我想不出会受此影响的应用程序。

【讨论】:

  • 我已经接受了一个答案,但我认为这实际上是最明确的说法。谢谢。
【解决方案2】:

我想知道约定是否有更多内容可以确定 snprintf 是否因为输入过多而失败。

这很简单。对于错误,返回负数。对于截断,返回一个 >= 大小的数字。无论您是否认为截断是一个错误,您都可以测试这两个条件并按照您认为合适的方式处理。

【讨论】:

  • 我不得不多次重读该规范,直到找到我想要的东西。我认为你所说的有问题,直到我重新阅读关于输出大小的行。当然,没有办法将成功运行与截断混淆,因为成功的返回值最多是大小。我没有考虑这一点,所以我认为截断被认为是错误并作为负值返回。
  • 对您的评论稍作更正。成功的返回值是小于 sizesize计数包含终止null,但返回值只返回字符输出,不包括终止null。
  • 哦,谢谢。我没有注意到尺寸和回报的差异。
【解决方案3】:

来自man page的描述:

返回值

成功返回后,这些函数返回打印的字符数(不包括用于结束输出到字符串的空字节)。

函数 snprintf() 和 vsnprintf() 写入的字节数不会超过 size 字节(包括终止空字节 ('\0'))。如果输出由于此限制而被截断,则返回值是字符数(不包括终止的空字节),如果有足够的空间可用,这些字符将被写入最终字符串。因此,大小或更大的返回值意味着输出被截断。 (另请参阅下面的注释。)

如果遇到输出错误,则返回负值。

所以对于你的情况:

Maxlen > 0,在发生错误之前没有写入任何字符,snprintf 保释

snprintf 返回一个负数。哪个负数无关紧要。

Maxlen > 0,所有字符都已写入,出现某种错误,snprintf 退出

snprintf 返回一个负数。哪个负数无关紧要。错误就是错误,在它发生之前写入了多少字节都没有关系。

【讨论】:

    【解决方案4】:

    作为进一步的评论,当反复使用 snprintf 以增量方式构建字符串时(例如,在循环中) - 或任何必须确定结果字符串长度的地方,我建议这是一种“安全”的方法......

    int foo( char * b, size_t bLen ) {
      size_t sLen = 0;
      for(int i = 0; i < 1000; i++) {
        int res = snprintf(&b[sLen], bLen - sLen, "%d. A string ", sLen);
        // sLen:         Inc by +ive res,      (upto safe maximum)        
        sLen = std::min( sLen + (res>0?res:0), (bLen?bLen-1:bLen) );
      }
      return sLen;
    }
    

    snprintf 之后的行将安全地递增字符串长度变量 (sLen),忽略负结果 (res) 并通过限制缓冲区长度 (bLen) 来防止溢出。 我在示例中使用了 C++“min”,但堆栈溢出提供了“C”替代方案:MIN and MAX in C

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-07-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-01-03
      • 1970-01-01
      相关资源
      最近更新 更多