snprintf и Visual Studio 2010 - PullRequest
       64

snprintf и Visual Studio 2010

99 голосов
/ 26 мая 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 год?

Ответы [ 6 ]

84 голосов
/ 03 января 2012

Краткая история: 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
32 голосов
/ 26 мая 2010

snprintf не является частью C89. Стандартно только в C99. У Microsoft нет плана поддержки C99 .

(но это также стандартно в C ++ 0x ...!)

См. Другие ответы ниже для обхода проблемы.

8 голосов
/ 28 октября 2015

Если вам не нужно возвращаемое значение, вы также можете просто определить snprintf как _snprintf_s

#define snprintf(buf,len, format,...) _snprintf_s(buf, len,len, format, __VA_ARGS__)
3 голосов
/ 26 мая 2010

Я считаю, что эквивалент Windows sprintf_s

2 голосов
/ 02 июля 2015

Другая безопасная замена snprintf() и vsnprintf() предоставляется ffmpeg. Вы можете проверить источник здесь (рекомендуется).

1 голос
/ 22 декабря 2016

Я пробовал код @Valentin Milea, но у меня есть ошибки нарушения доступа. Единственная вещь, которая работала для меня, была реализация Insane Coding: http://asprintf.insanecoding.org/

В частности, я работал с унаследованным кодом VC ++ 2008. Из реализации Insane Coding (можно скачать по ссылке выше) я использовал три файла: asprintf.c, asprintf.h и vasprintf-msvc.c. Другие файлы были для других версий MSVC.

[РЕДАКТИРОВАТЬ] Для полноты их содержание выглядит следующим образом:

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);
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...