Snprintf 和 VisualStudio2010

我很不幸地在一个项目中使用 VS 2010,并且注意到下面的代码仍然没有使用不符合标准的编译器来构建:

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


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


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


return 0;
}

(错误编译失败: C3861: ‘ snprintf’: 标识符未找到)

我记得在 VS 2005的时候就是这样的情况,我很震惊地看到它仍然没有被修复。

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

80747 次浏览

I believe the Windows equivalent is sprintf_s

snprintf is not part of C89. It's standard only in C99. Microsoft has no plan supporting C99.

(But it's also standard in C++0x...!)

See other answers below for a workaround.

Short story: Microsoft has finally implemented snprintf in Visual Studio 2015. On earlier versions you can simulate it as below.


Long version:

Here is the expected behavior for snprintf:

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

Writes at most buf_size - 1 characters to a buffer. The resulting character string will be terminated with a null character, unless buf_size is zero. If buf_size is zero, nothing is written and buffer may be a null pointer. The return value is the number of characters that would have been written assuming unlimited buf_size, not counting the terminating null character.

Releases prior to Visual Studio 2015 didn't have a conformant implementation. There are instead non-standard extensions such as _snprintf() (which doesn't write null-terminator on overflow) and _snprintf_s() (which can enforce null-termination, but returns -1 on overflow instead of the number of characters that would have been written).

Suggested fallback for VS 2005 and up:

#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

Another safe replacement of snprintf() and vsnprintf() is provided by ffmpeg. You can checkout the source here (suggested).

If you don't need the return value, you could also just define snprintf as _snprintf_s

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

I tried @Valentin Milea's code but I've got access violation errors. The only thing that worked for me was Insane Coding's implementation: http://asprintf.insanecoding.org/

Specifically, I was working with VC++2008 legacy code. From Insane Coding's implementation (can be downloaded from the link above), I used three files: asprintf.c, asprintf.h and vasprintf-msvc.c. Other files were for other versions of MSVC.

[EDIT] For completeness, their contents are as follows:

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);
}

Usage (part of test.c provided by Insane Coding):

#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);
}
}