Snprintf и Visual Studio 2010
Мне так жаль, что вы застряли, используя 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, и я был потрясен, увидев, что он все еще не исправлен.
Кто-нибудь знает, планирует ли Microsoft планировать свои стандартные библиотеки C в 2010 году?
Ответы
Ответ 1
Рассказ: Microsoft наконец-то реализовала snprintf в Visual Studio 2015. В более ранних версиях вы можете имитировать его, как показано ниже.
Длинная версия:
Вот ожидаемое поведение для snprintf:
int snprintf( char* buffer, std::size_t buf_size, const char* format, ... );
Записывает не более символов buf_size - 1
в буфер. Результирующий символьная строка будет завершена нулевым символом, если только buf_size
равно нулю. Если buf_size
равно нулю, ничего не записывается и buffer
может быть нулевым указателем. Возвращаемое значение - это количество персонажей, которые были бы написаны при условии неограниченного buf_size
, не считая завершающего нулевого символа.
Релизы, предшествующие Visual Studio 2015, не имели согласованной реализации. Вместо этого существуют нестандартные расширения, такие как _snprintf()
(который не записывает нуль-терминатор при переполнении) и _snprintf_s()
(который может принудительно принудительно завершать нуль, но возвращает -1 при переполнении вместо количества символов, которое были написаны).
Предлагаемый резерв для VS 2005 и выше:
#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
Ответ 2
snprintf
не является частью C89. Он стандартный только в C99. Microsoft не поддерживает план C99.
(Но он также стандартный в С++ 0x...!)
См. другие ответы ниже для обходного пути.
Ответ 3
Если вам не требуется возвращаемое значение, вы также можете просто определить snprintf как _snprintf_s
#define snprintf(buf,len, format,...) _snprintf_s(buf, len,len, format, __VA_ARGS__)
Ответ 4
Я считаю, что эквивалент Windows sprintf_s
Ответ 5
Другая безопасная замена snprintf()
и vsnprintf()
обеспечивается ffmpeg. Вы можете проверить источник здесь (рекомендуется).
Ответ 6
Я попробовал код @Valentin Milea, но у меня были ошибки доступа. Единственное, что сработало для меня, это реализация Insane Coding: http://asprintf.insanecoding.org/
В частности, я работал с устаревшим кодом VС++ 2008. Из реализации Insane Coding (можно скачать по ссылке выше) я использовал три файла: asprintf.c
, asprintf.h
и vasprintf-msvc.c
. Другие файлы были для других версий MSVC.
[EDIT] Для полноты их содержимое выглядит следующим образом:
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);
}
Использование (часть test.c
, предоставляемое 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);
}
}