代码之家  ›  专栏  ›  技术社区  ›  Navaneeth K N

在跨平台应用中使用snprintf

  •  9
  • Navaneeth K N  · 技术社区  · 14 年前

    我正在编写一个C程序,该程序预计将与所有主要编译器一起编译。目前我正在Linux机器上开发GCC,在提交代码之前将在MSVC上编译。为了使交叉编译更容易,我使用 -ansi -pedantic 旗帜。直到我开始使用 snprintf C89标准中没有。GCC可以在没有 ANSI 但是MSVC总是会失败,因为它不支持C99。

    所以我做了一些事情,

    #ifdef WIN32 
    #define snprintf sprintf_s
    #endif
    

    这很有效,因为 SNPrTNF sprintf_s 具有相同的签名。我想知道这是正确的方法吗?

    5 回复  |  直到 8 年前
        1
  •  -8
  •   Qix - MONICA WAS MISTREATED    10 年前

    不。 你的方法注定要失败。

    sqrt cos 有相同的原型。您认为您可以在程序中交换它们,并从更改前后获得相同的行为吗?


    你可能应该自己写 snprintf 或从Internet下载实现( google is your friend )在Linux和Windows中都使用它。

        2
  •  16
  •   DevSolar    9 年前

    我发现 this 关于使用 _snprintf() 另一种选择是,如果缓冲区溢出保护实际触发,则涉及gotchas。从我一眼就能看出,类似的警告也适用于 sprintf_s .

    你看到问题了吗?在Linux版本中,输出总是以空结尾。在MSVC中,不是这样。

    更微妙的是 size Linux和中的参数 count MSVC中的参数。前者是包括终止空值在内的输出缓冲区大小,后者是要存储的最大字符数,不包括终止空值。

    哦,别忘了给微软发邮件,要求他们支持当前的语言标准。(我知道他们已经宣布他们没有计划支持C99,但无论如何都要骚扰他们。他们活该。)

    最重要的是,如果你想真正安全地玩,你必须提供你自己的 snprintf() (包装纸 ySnPrftf() sprintf_s() 了解他们的非标准行为)。

        3
  •  13
  •   eci    11 年前

    如果你小心的话,你的建议可以奏效。问题是这两个函数的行为都略有不同,如果这对您来说不是问题,那么您可以走了,否则请考虑包装函数:

    MSVCS之间的差异 _snprintf 以及官方C99(GCC、Clang) snprintf :

    返回值:

    • msvc:如果缓冲区大小不足以写入所有内容,则返回-1(不包括终止NULL!)
    • gcc:返回如果缓冲区足够大就可以写入的字符数

    写入字节:

    • msvc:尽可能多地写,如果没有剩余空间,不要在结尾处写空值
    • gcc:尽可能多地写入,始终写入终止空值(异常:缓冲区大小=0)

    有趣 %n 微妙: 如果你使用 %N 在您的代码中,MSVC将使其保持统一!如果它因为缓冲区大小太小而停止解析,那么gcc将始终写入在缓冲区足够大的情况下写入的字节数。

    所以我的建议是编写自己的包装函数 mysnprintf 使用 vsnprintf / _vsnprintf 它提供相同的返回值,并在两个平台上写入相同的字节(请注意: %N 更难修复)。

        4
  •  1
  •   Paul Humphreys    8 年前

    您可以打开MSVC的nul特殊文件并写入该文件。它总是告诉您需要多少字节,并且不会写入任何内容。像这样:

    int main (int argc, char* argv[]) { 
      FILE* outfile = fopen("nul", "wb");
      int written;
    
      if(outfile == NULL) {
        fputs ("could not open 'nul'", stderr);
      }
      else {
        written = fprintf(outfile, "redirect to /dev/null");
        fclose(outfile);
        fprintf(stdout, "didn't write %d characters", written);
      }
    
      return 0;
    }
    

    然后您应该知道要分配多少字节才能成功地使用sprintf。

        5
  •  0
  •   donthaveanaccount    12 年前

    最完整的答案(如果你愿意,可以改进),把它贴在标签上

    #if __PLATFORM_WIN_ZERO_STANDARD__
    
        static inline
        int LIBSYS_SNPRINTF(char * str, size_t size, const char * format, ...)
        {
            int retval;
            va_list ap;
            va_start(ap, format);
            retval = _vsnprintf(str, size, format, ap);
            va_end(ap);
            return retval;
        }
    
        static inline
        int LIBSYS_VASPRINTF(char **ret, char * format, va_list ap)
        {
            int wanted = vsnprintf(*ret = NULL, 0, format, ap);
            if((wanted > 0) && ((*ret = LIBSYS_MALLOC(1 + wanted)) != NULL)) {
                return vsprintf(*ret, format, ap);
            }
            return wanted;
        }
    
        static inline
        int LIBSYS_ASPRINTF(char **ret, char * format, ...)
        {
            int retval;
            va_list ap;
            va_start(ap, format);
            retval = LIBSYS_VASPRINTF(ret, format, ap);
            va_end(ap);
            return retval;
        }
    
    #else
        #define LIBSYS_SNPRINTF snprintf
        #define LIBSYS_VASPRINTF vasprintf
        #define LIBSYS_ASPRINTF asprintf
    #endif