snprintf 및 Visual Studio 2010


102

불행히도 프로젝트에 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
... 아니면 그냥 "#DEFINE의 현재 snprintf의 _snprintf"할 수
페르난도 곤잘레스 산체스

4
... 할 수 있지만 불행히도 _snprintf ()는 null 종료를 보장하지 않으므로 snprintf ()와 동일하지 않습니다.
Andy Krouwel 2015 년

좋습니다. _snprintf ()를 사용하기 전에 0으로 memset해야합니다. 또한 나는 당신에게 동의합니다. MSVC에서 개발하는 것은 끔찍합니다. 오류도 지옥처럼 혼란 스럽습니다.
Owl

답변:


88

짧은 이야기 : Microsoft는 마침내 Visual Studio 2015에서 snprintf를 구현했습니다. 이전 버전에서는 아래와 같이 시뮬레이션 할 수 있습니다.


긴 버전 :

snprintf의 예상 동작은 다음과 같습니다.

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

buf_size - 1버퍼 에 최대 문자를 씁니다 . 결과 문자열 buf_size은 0이 아니면 널 문자로 종료 됩니다. 경우 buf_sizeIS는 제로, 아무것도 기록되지 않고 buffer널 포인터가 될 수있다. 반환 값은 buf_size종료 null 문자를 계산하지 않고 unlimited라고 가정하여 작성되었을 문자 수입니다.

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

오버플로에 필요한 0으로 문자열이 항상 종료되는 것은 아닙니다. c99_vsnprintf의 두 번째 if는 다음과 같아야합니다. if (count == -1) {if (size> 0) str [size-1] = 0; count = _vscprintf (형식, ap); }
Lothar 2014

1
@Lothar : 버퍼는 항상 null로 종료됩니다. MSDN에 따르면 : "_TRUNCATE를 전달하여 문자열 잘림이 활성화 된 경우 이러한 함수는 필요한만큼만 문자열을 복사하여 대상 버퍼를 null로 종료하고 성공적으로 반환합니다."
Valentin Milea 2014

2
2014 년 6 월 현재, Visual Studio에서 "완전한"C99 지원은 아직 없습니다. 업데이트 2에도 불구 하고이 블로그 는 MSVC 2013에 대한 C99 지원 개요를 제공합니다. snprintf () 제품군 함수는 이제 C ++ 11 표준의 일부입니다. , MSVC는 C ++ 11 구현에서 clang 및 gcc보다 뒤처집니다!
fnisi

2
VS2014에서는 snprintf 및 vsnprintf가 포함 된 C99 표준이 추가되었습니다. blogs.msdn.com/b/vcblog/archive/2014/06/18/…을 참조하십시오 .
vulcan raven

1
Mikael Lepistö : 정말요? 나를 위해 _snprintf는 _CRT_SECURE_NO_WARNINGS를 활성화하는 경우에만 작동합니다. 이 해결 방법은 해당 단계없이 잘 작동합니다.
FvD

33

snprintfC89의 일부가 아닙니다. C99에서만 표준입니다. Microsoft는 C99를 지원하는 계획없습니다 .

(하지만 C ++ 0x에서도 표준입니다 ...!)

해결 방법은 아래의 다른 답변을 참조하십시오.


5
그러나 snprintf 및 _snprintf 동작에 차이가 있기 때문에 좋은 해결 방법은 아닙니다. _snprintf는 버퍼 공간이 부족한 경우 널 종료자를 지연 처리합니다.
Andrew

7
@DeadMG-잘못되었습니다. cl.exe는 파일을 C 코드로 컴파일하도록 컴파일러에 지시하는 / Tc 옵션을 지원합니다. 또한 MSVC는 표준 C 라이브러리 버전과 함께 제공됩니다.
Andrew

3
@DeadMG-그러나 C90 표준과 몇 비트의 C99를 지원하므로 C 컴파일러가됩니다.
Andrew

15
1990 년에서 1999 년 사이에 사는 경우에만.
Puppy

6
-1, Microsoft _snprintf는 다르게 작동하는 안전하지 않은 기능 snprintf이므로 (반드시 null 종결자를 추가하지 않아도 됨)이 답변에 제공된 조언은 오해의 소지가 있고 위험합니다.
interjay

8

반환 값이 필요하지 않은 경우 snprintf를 _snprintf_s로 정의 할 수도 있습니다.

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

3

나는 Windows에 상응하는 것이 sprintf_s


7
sprintf_s와 다르게 작동합니다 snprintf.
interjay

특히 sprintf_s 문서는 "버퍼가 인쇄되는 텍스트에 비해 너무 작 으면 버퍼가 빈 문자열로 설정됩니다"라고 말합니다. 반대로 snprintf는 잘린 문자열을 출력에 씁니다.
Andrew Bainbridge 2014 년

2
@AndrewBainbridge-문서를 잘랐습니다. 전체 문장은 "버퍼가 인쇄되는 텍스트에 비해 너무 작 으면 버퍼가 빈 문자열로 설정되고 유효하지 않은 매개 변수 핸들러가 호출됩니다."입니다. 잘못된 매개 변수 핸들의 기본 동작은 프로그램을 종료하는 것입니다. _s 계열로 자르기를 원하면 snprintf_s 및 _TRUNCATE 플래그를 사용해야합니다. 예, _s 함수가 편리한 절단 방법을 제공하지 않는 것은 유감입니다. 반면에 _s 함수는 템플릿 매직을 사용하여 버퍼 크기를 추론합니다.
Bruce Dawson


1

@Valentin Milea의 코드를 시도했지만 액세스 위반 오류가 있습니다. 나를 위해 일한 유일한 것은 Insane Coding의 구현이었습니다. http://asprintf.insanecoding.org/

특히 VC ++ 2008 레거시 코드로 작업하고있었습니다. Insane Coding의 구현 (위 링크에서 다운로드 가능)에서 asprintf.c, asprintf.hvasprintf-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.cInsane 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);
  }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.