我正在编写一个C程序,预计将与所有主要编译器一起编译。目前我在Linux机器上开发GCC,并在提交代码之前在MSVC上编译。为了简化交叉编译,我使用-ansi
和-pedantic
标志进行编译。这很有效,直到我开始使用C89标准中没有的snprintf
。 GCC可以在没有-ansi
开关的情况下编译它,但MSVC将始终失败,因为它没有C99支持。
所以我做了类似的事,
#ifdef WIN32
#define snprintf sprintf_s
#endif
这很有效,因为snprintf
和sprintf_s
具有相同的签名。我想知道这是正确的方法吗?
答案 0 :(得分:16)
我发现this使用_snprintf()
作为替代,并且如果缓冲区溢出保护实际触发,则涉及陷阱。从我能够快速浏览的内容来看,类似的警告适用于sprintf_s
。
你能看到问题吗?在Linux版本中,输出始终以空值终止。在MSVC中,它不是。
更加微妙的是Linux中的
size
参数与MSVC中的count
参数之间的差异。前者是输出缓冲区的大小,包括终止空值,后者是要存储的最大字符数,排除了终止空值。
哦,不要忘记向Microsoft发送邮件,要求他们支持当前的语言标准。 (我知道他们已经宣布他们没有计划支持C99,但无论如何都要打扰他们。他们应该得到它。)
最重要的是,如果您想要真正安全地播放它,您必须提供自己的snprintf()
(_snprintf()
或sprintf_s()
的包装,以捕捉其非标准行为)对于MSVC。
答案 1 :(得分:13)
如果你小心,你的建议可以奏效。问题是这两个函数的行为略有不同,如果这对你来说不是问题,你就好了,否则考虑一个包装函数:
MSVC _snprintf
与官方C99(gcc,clang)snprintf
之间的差异:
返回值:
写入字节:
有趣%n
微妙:
如果您在代码中使用%n
,MSVC会将其统一化!如果由于缓冲区大小很小而停止解析,GCC将始终写入如果缓冲区足够大就会写入的字节数。
所以我的提议是使用mysnprintf
/ vsnprintf
编写自己的包装函数_vsnprintf
,它提供相同的返回值,并在两个平台上写入相同的字节(注意:{{ 1}}更难修复。
答案 2 :(得分:1)
您可以打开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。
答案 3 :(得分:0)
最完整的答案(如果您愿意,可以改进),将其放入贴纸
#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
答案 4 :(得分:-8)
不。您的方法注定要失败。
sqrt
和cos
具有相同的原型。您是否认为可以在程序中交换它们并在更改之前/之后获得相同的行为?
您可能应该编写自己的snprintf
,或者从Internet(google is your friend)下载实现,并在Linux和Windows中使用它们。