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 -ansi
and -pedantic
flags. This worked well until I started using snprintf
which is not available in C89 standard. GCC can compile this without the -ansi
switch but MSVC will fail always as it doesn't have C99 support.
我正在编写一个C程序,预计将与所有主要编译器一起编译。目前我在Linux机器上开发GCC,并在提交代码之前在MSVC上编译。为了使交叉编译变得容易,我正在使用-ansi和-pedantic标志进行编译。这很有效,直到我开始使用c89标准中没有的snprintf。 GCC可以在没有-ansi开关的情况下编译它,但MSVC总是会失败,因为它没有C99支持。
So I did something like,
所以我做了类似的事情,
#ifdef WIN32
#define snprintf sprintf_s
#endif
This works well because snprintf
and sprintf_s
has same signatures. I am wondering is this the correct approach?
这很有效,因为snprintf和sprintf_s具有相同的签名。我想知道这是正确的做法吗?
5 个解决方案
#1
-8
No. Your approach is doomed to failure.
不,你的方法注定要失败。
sqrt
and cos
have 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,或从互联网上下载一个实现(谷歌是你的朋友),并在Linux和Windows中使用它。
#2
15
I found this on 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.
你能看到问题吗?在Linux版本中,输出始终以null结尾。在MSVC中,事实并非如此。
Even more subtle is the difference between the
size
parameter in Linux andcount
parameter 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中的size参数和MSVC中的count参数之间的差异。前者是输出缓冲区的大小,包括终止空值,后者是要存储的最大字符数,这排除了终止空值。
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()捕捉它们的非标准行为的包装器)。
#3
12
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 _snprintf
and 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 %n
subtlety: If you use %n
in 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 mysnprintf
using vsnprintf
/ _vsnprintf
which gives same return values and writes the same bytes on both platforms (be careful: %n
is more difficult to fix).
所以我的建议是使用vsnprintf / _vsnprintf编写自己的包装函数mysnprintf,它给出相同的返回值并在两个平台上写入相同的字节(注意:%n更难修复)。
#4
1
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。
#5
0
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
#1
-8
No. Your approach is doomed to failure.
不,你的方法注定要失败。
sqrt
and cos
have 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,或从互联网上下载一个实现(谷歌是你的朋友),并在Linux和Windows中使用它。
#2
15
I found this on 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.
你能看到问题吗?在Linux版本中,输出始终以null结尾。在MSVC中,事实并非如此。
Even more subtle is the difference between the
size
parameter in Linux andcount
parameter 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中的size参数和MSVC中的count参数之间的差异。前者是输出缓冲区的大小,包括终止空值,后者是要存储的最大字符数,这排除了终止空值。
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()捕捉它们的非标准行为的包装器)。
#3
12
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 _snprintf
and 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 %n
subtlety: If you use %n
in 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 mysnprintf
using vsnprintf
/ _vsnprintf
which gives same return values and writes the same bytes on both platforms (be careful: %n
is more difficult to fix).
所以我的建议是使用vsnprintf / _vsnprintf编写自己的包装函数mysnprintf,它给出相同的返回值并在两个平台上写入相同的字节(注意:%n更难修复)。
#4
1
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。
#5
0
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