【问题标题】:snprintf and Visual Studio 2010snprintf 和 Visual Studio 2010
【发布时间】:2010-05-26 18:25:12
【问题描述】:

我很不幸在项目中使用 VS 2010 时卡住了,并注意到以下代码仍然无法使用不符合标准的编译器构建:

#include <stdio.h>
#include <stdlib.h>

int main (void)
{
    char buffer[512];

    snprintf(buffer, sizeof(buffer), "SomeString");

    return 0;
}

(编译失败,错误为:C3861: 'snprintf': identifier not found)

我记得早在 VS 2005 中就是这种情况,我很震惊地看到它仍未修复。

有人知道微软是否有计划将他们的标准 C 库迁移到 2010 年?

【问题讨论】:

  • ... 或者您可以执行“#define snprintf _snprintf”
  • ...你可以,但不幸的是 _snprintf() 与 snprintf() 不同,因为它不能保证空终止。
  • 好的,所以您需要在使用 _snprintf() 之前将其设置为零。我也同意你的看法。在 MSVC 下开发很糟糕。这些错误也令人困惑。

标签: c visual-studio-2010


【解决方案1】:

简短的故事:微软终于在 Visual Studio 2015 中实现了 snprintf。在早期版本中,您可以如下模拟它。


加长版:

这是 snprintf 的预期行为:

int snprintf( char* buffer, std::size_t buf_size, const char* format, ... );

最多将buf_size - 1 个字符写入缓冲区。所结果的 字符串将以空字符终止,除非 buf_size 为零。如果buf_size 为零,则不写入任何内容并且 buffer 可能是一个空指针。返回值是个数 假设无限 buf_size 将被写入的字符, 不计算终止的空字符。

Visual Studio 2015 之前的版本没有一致的实现。取而代之的是非标准扩展,例如_snprintf()(在溢出时不写空终止符)和_snprintf_s()(可以强制空终止,但在溢出时返回-1而不是字符数已经写好了)。

VS 2005 及更高版本的建议备用:

#if defined(_MSC_VER) && _MSC_VER < 1900

#define snprintf c99_snprintf
#define vsnprintf c99_vsnprintf

__inline int c99_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap)
{
    int count = -1;

    if (size != 0)
        count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap);
    if (count == -1)
        count = _vscprintf(format, ap);

    return count;
}

__inline int c99_snprintf(char *outBuf, size_t size, const char *format, ...)
{
    int count;
    va_list ap;

    va_start(ap, format);
    count = c99_vsnprintf(outBuf, size, format, ap);
    va_end(ap);

    return count;
}

#endif

【讨论】:

  • 这并不总是以溢出时所需的 0 终止字符串。 c99_vsnprintf 中的第二个 if 必须是: if (count == -1) { if (size > 0) str[size-1] = 0;计数 = _vscprintf(格式,ap); }
  • @Lothar:缓冲区始终以空值结尾。根据 MSDN:“如果通过传递 _TRUNCATE 启用字符串截断,这些函数将仅复制尽可能多的字符串,使目标缓冲区以空值终止,并成功返回”。
  • 截至 2014 年 6 月,Visual Studio 中仍然没有“完整”C99 支持,即使是更新 2。This blog 提供了 MSVC 2013 的 C99 支持简介。因为 snprintf() 系列函数是现在是 C++11 标准的一部分,MSVC 在 C++11 实现中落后于 clang 和 gcc!
  • 在 VS2014 中,添加了带有 snprintf 和 vsnprintf 的 C99 标准。见blogs.msdn.com/b/vcblog/archive/2014/06/18/…
  • Mikael Lepistö:真的吗?对我来说,_snprintf 只有在我启用 _CRT_SECURE_NO_WARNINGS 时才有效。没有该步骤,此解决方法可以正常工作。
【解决方案2】:

snprintf 不是 C89 的一部分。它仅在 C99 中是标准的。微软有no plan supporting C99

(但它在 C++0x 中也是标准的......!)

请参阅下面的其他答案以了解解决方法。

【讨论】:

  • 这不是一个好的解决方法,但是......因为 snprintf 和 _snprintf 行为存在差异。 _snprintf 在处理缓冲区空间不足时延迟处理空终止符。
  • @DeadMG - 错误。 cl.exe 支持 /Tc 选项,该选项指示编译器将文件编译为 C 代码。此外,MSVC 附带一个标准 C 库版本。
  • @DeadMG - 但是,它确实支持 C90 标准以及 C99 的一些位,使其成为 C 编译器。
  • 仅当您生活在 1990 年到 1999 年之间。
  • -1,Microsoft 的 _snprintf 是一个不安全的函数,其行为与 snprintf 不同(它不一定添加空终止符),因此此答案中给出的建议具有误导性和危险性。
【解决方案3】:

如果您不需要返回值,您也可以将 snprintf 定义为 _snprintf_s

#define snprintf(buf,len, format,...) _snprintf_s(buf, len,len, format, __VA_ARGS__)

【讨论】:

    【解决方案4】:

    我相信 Windows 等价物是 sprintf_s

    【讨论】:

    • sprintf_s 的行为与 snprintf 不同。
    • 特别是 sprintf_s 文档说,“如果缓冲区对于正在打印的文本来说太小,则缓冲区设置为空字符串”。相反,snprintf 将截断的字符串写入输出。
    • @AndrewBainbridge -- 你截断了文档。完整的句子是“如果缓冲区对于正在打印的文本来说太小,则缓冲区被设置为空字符串并调用无效的参数处理程序。”无效参数句柄的默认行为是终止您的程序。如果你想用 _s 系列截断,那么你需要使用 snprintf_s 和 _TRUNCATE 标志。是的,不幸的是 _s 函数没有提供一种方便的截断方法。另一方面,_s 函数确实使用模板魔法来推断缓冲区大小,这非常棒。
    【解决方案5】:

    ffmpeg 提供了snprintf()vsnprintf() 的另一个安全替换。您可以查看source here(建议)。

    【讨论】:

      【解决方案6】:

      我尝试了@Valentin Milea 的代码,但我遇到了访问冲突错误。唯一对我有用的是 Insane Coding 的实现:http://asprintf.insanecoding.org/

      具体来说,我正在使用 VC++2008 遗留代码。在 Insane Coding 的实现中(可以从上面的链接下载),我使用了三个文件:asprintf.casprintf.hvasprintf-msvc.c。其他文件用于其他版本的 MSVC。

      [编辑]为完整起见,其内容如下:

      asprintf.h:

      #ifndef INSANE_ASPRINTF_H
      #define INSANE_ASPRINTF_H
      
      #ifndef __cplusplus
      #include <stdarg.h>
      #else
      #include <cstdarg>
      extern "C"
      {
      #endif
      
      #define insane_free(ptr) { free(ptr); ptr = 0; }
      
      int vasprintf(char **strp, const char *fmt, va_list ap);
      int asprintf(char **strp, const char *fmt, ...);
      
      #ifdef __cplusplus
      }
      #endif
      
      #endif
      

      asprintf.c:

      #include "asprintf.h"
      
      int asprintf(char **strp, const char *fmt, ...)
      {
        int r;
        va_list ap;
        va_start(ap, fmt);
        r = vasprintf(strp, fmt, ap);
        va_end(ap);
        return(r);
      }
      

      vasprintf-msvc.c:

      #include <stdio.h>
      #include <stdlib.h>
      #include <limits.h>
      #include "asprintf.h"
      
      int vasprintf(char **strp, const char *fmt, va_list ap)
      {
        int r = -1, size = _vscprintf(fmt, ap);
      
        if ((size >= 0) && (size < INT_MAX))
        {
          *strp = (char *)malloc(size+1); //+1 for null
          if (*strp)
          {
            r = vsnprintf(*strp, size+1, fmt, ap);  //+1 for null
            if ((r < 0) || (r > size))
            {
              insane_free(*strp);
              r = -1;
            }
          }
        }
        else { *strp = 0; }
      
        return(r);
      }
      

      用法(Insane Coding 提供的test.c 的一部分):

      #include <stdio.h>
      #include <stdlib.h>
      #include "asprintf.h"
      
      int main()
      {
        char *s;
        if (asprintf(&s, "Hello, %d in hex padded to 8 digits is: %08x\n", 15, 15) != -1)
        {
          puts(s);
          insane_free(s);
        }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-07-30
        • 2011-01-26
        • 2011-10-03
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多