C语言 在跨平台应用程序中使用 snprintf
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3976306/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Using snprintf in a cross-platform application
提问by Navaneeth K N
I am writing a C program that is expected to be compiled with all major compilers. Currently I am developing on GCC on a linux machine and will compile on MSVC before committing the code. To make the cross-compiling easy, I am compiling with -ansiand -pedanticflags. This worked well until I started using snprintfwhich is not available in C89 standard. GCC can compile this without the -ansiswitch but MSVC will fail always as it doesn't have C99 support.
我正在编写一个 C 程序,该程序预计可以使用所有主要编译器进行编译。目前我正在 Linux 机器上的 GCC 上进行开发,并将在提交代码之前在 MSVC 上进行编译。为了使交叉编译更容易,我使用-ansi和-pedantic标志进行编译。在我开始使用snprintfC89 标准中不可用之前,这很有效。GCC 可以在没有-ansi开关的情况下编译它,但 MSVC 总是会失败,因为它没有 C99 支持。
So I did something like,
所以我做了类似的事情,
#ifdef WIN32
#define snprintf sprintf_s
#endif
This works well because snprintfand sprintf_shas same signatures. I am wondering is this the correct approach?
这种运作良好,因为snprintf与sprintf_s具有相同签名。我想知道这是正确的方法吗?
采纳答案by pmg
No.Your approach is doomed to failure.
不。你的方法注定要失败。
sqrtand coshave the same prototype. Do you think you can swap them in a program and obtain the same behaviour from before / after the change?
sqrt并cos拥有相同的原型。您认为您可以在程序中交换它们并在更改之前/之后获得相同的行为吗?
You probably should write your own snprintf, or download an implementation from the internet (google is your friend) and use that both in Linux and Windows.
您可能应该自己编写snprintf,或者从 Internet 下载一个实现(google 是您的朋友)并在 Linux 和 Windows 中使用它。
回答by DevSolar
I found thison using _snprintf()as an alternative, and the gotchas involved if the buffer overrun protection actually triggers. From what I could see at a quick glance, similar caveats apply to sprintf_s.
我发现这是_snprintf()作为替代方案使用的,如果缓冲区溢出保护实际触发,则涉及的问题。从我一眼就能看出,类似的警告适用于sprintf_s.
Can you see the problem? In the Linux version, the output is always null-terminated. In MSVC, it's not.
Even more subtle is the difference between the
sizeparameter in Linux andcountparameter in MSVC. The former is the size of the output buffer including the terminating null and the latter is the maximum count of characters to store, which excludes the terminating null.
你能看出问题吗?在 Linux 版本中,输出始终以空字符结尾。在 MSVC 中,它不是。
更微妙的是
sizeLinux 中的count参数和 MSVC 中的参数之间的区别。前者是输出缓冲区的大小,包括终止空值,后者是要存储的最大字符数,不包括终止空值。
Oh, and don't forget to send a mail to Microsoft demanding they support current language standards. (I know they already announced they have no plan to support C99, but bugger them anyway. They deserve it.)
哦,别忘了向 Microsoft 发送邮件,要求他们支持当前的语言标准。(我知道他们已经宣布他们没有支持 C99 的计划,但无论如何还是打扰他们。他们应得的。)
Bottom line, if you want to play it really safe, you'll have to provide your own snprintf()(a wrapper around _snprintf()or sprintf_s()catching their non-standard behaviour) for MSVC.
最重要的是,如果你想真正安全地玩它,你必须为 MSVC提供你自己的snprintf()(一个包装器_snprintf()或sprintf_s()捕捉它们的非标准行为)。
回答by eci
Your proposal can work if you are being careful. The problem is that both function behave slightly different, if that is not a problem for you, you are good to go, otherwise think about a wrapper function:
如果您小心谨慎,您的建议可能会奏效。问题是这两个函数的行为略有不同,如果这对您来说不是问题,那么您就可以开始了,否则请考虑包装函数:
Differences between MSVCs _snprintfand official C99 (gcc,clang) snprintf:
MSVC_snprintf和官方 C99 (gcc,clang)之间的差异snprintf:
Return value:
返回值:
- MSVC: return -1 if buffer size not enough to write everything (not including terminating null!)
- GCC: return number of characters that would have been written if buffer large enough
- MSVC:如果缓冲区大小不足以写入所有内容,则返回 -1(不包括终止 null!)
- GCC:如果缓冲区足够大,则返回将写入的字符数
Written bytes:
写入字节:
- MSVC: write as much as possible, do not write NULL at end if no space left
- GCC: write as much as possible, always write terminating NULL (exception: buffer_size=0)
- MSVC:尽量多写,没空就不要在末尾写NULL
- GCC:尽可能多写,总是写终止NULL(例外:buffer_size=0)
Interesting %nsubtlety:
If you use %nin your code, MSVC will leave it unitialized! if it it stops parsing because buffer size is to small, GCC will always write number of bytes which would have been written if buffer would have been large enough.
有趣的%n微妙之处:如果你%n在你的代码中使用,MSVC 会将它单元化!如果它因为缓冲区大小太小而停止解析,则 GCC 将始终写入如果缓冲区足够大就会写入的字节数。
So my proposal would be to write your own wrapper function mysnprintfusing vsnprintf/ _vsnprintfwhich gives same return values and writes the same bytes on both platforms (be careful: %nis more difficult to fix).
所以我的建议是mysnprintf使用vsnprintf/编写自己的包装函数_vsnprintf,它提供相同的返回值并在两个平台上写入相同的字节(小心:%n更难修复)。
回答by Paul Humphreys
You could open the NUL special file for MSVC and write to that. It will always tell you how many bytes are needed, and won't write to anything. Like so:
您可以打开 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;
}
You then should know how many bytes to allocate to use sprintf sucessfully.
然后您应该知道分配多少字节才能成功使用 sprintf。
回答by donthaveanaccount
the most complete answer (you can improve if you wish), put that into a sticker
最完整的答案(如果你愿意,可以改进),把它贴在贴纸上
#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

