snprintf ()는 항상 null로 종료됩니까?


82

snprintf는 항상 대상 버퍼를 종료하는 null입니까?

즉, 이것으로 충분합니까?

char dst[10];

snprintf(dst, sizeof (dst), "blah %s", somestr);

아니면 somestr이 충분히 길다면 이렇게해야합니까?

char dst[10];

somestr[sizeof (dst) - 1] = '\0';
snprintf(dst, sizeof (dst) - 1, "blah %s", somestr);

나는 표준이 말하는 것과 표준 동작이 아닌 일부 대중적인 libc가 무엇을 할 수 있는지에 관심이 있습니다.


두 번째 예제에서 nul로 somestr 또는 dst를 종료한다는 의미입니까?
Hudson

@chux, Martin Ba는 수락 된 답변에서 그것을 다루었습니다. :)
Prof. Falken 2014 년

@chux 나는 그것이 좋았다고 생각합니다. 귀하의 의견은 dest i가 0이면 아무것도 쓰여지지 않는다는 것을 매우 분명하게 보여주었습니다. 나는 모든 의견을 동료 스택 오버 플라워와 대화 할 수있는 잠재적 인 변명으로 삼습니다. :)
Prof. Falken 2014 년

@Prof. Falken 댓글이 괜찮고 명시 적이라는 데 동의했지만 답변이 중복되었습니다. 내 리뷰에서 놓친 것뿐입니다.
chux - 분석 재개 모니카

stackoverflow.com/a/8712996/193892 Visual Studio는 이제 snprintf ()를
Prof. Falken

답변:


71

다른 답변이 설정으로 : 그것은 해야한다 :

snprintf... 결과를 문자열 버퍼에 기록합니다. (...) buf_size가 0이 아니면 널 문자로 종료됩니다.

따라서주의해야 할 것은 크기가 0 인 버퍼를 전달하지 않는 것입니다. 왜냐하면 (분명히) "nowhere"에 0을 쓸 수 없기 때문입니다.


그러나 조심 마이크로 소프트의 라이브러리가 없는 라는 함수를 snprintf대신 역사적으로 단지 라는 함수했다 _snprintf(참고 선도 밑줄) 추가하지 않습니다 종단 널 (null)입니다. 다음은 문서입니다 (VS 2012, ~~ VS 2013) :

http://msdn.microsoft.com/en-us/library/2ts7cx93%28v=vs.110%29.aspx

반환 값

len을 형식화 된 데이터 문자열의 길이 (종료 널을 포함하지 않음)로 지정하십시오. len 및 count는 _snprintf의 경우 바이트, _snwprintf의 경우 와이드 문자입니다.

  • len <count이면 len 문자가 버퍼에 저장되고 널 종료자가 추가되며 len이 리턴됩니다.

  • len = count이면 len 문자가 버퍼에 저장되고 널 종료자가 추가되지 않으며 len이 리턴됩니다.

  • len> count이면 count 문자가 버퍼에 저장되고 널 종료자가 추가되지 않고 음수 값이 리턴됩니다.

(...)

Visual Studio 2015 (VC14)는 준수 snprintf기능을 도입 한 것 같지만 선행 밑줄과 null 이 아닌 종료 동작이 있는 레거시 기능 이 여전히 존재합니다.

snprintf함수는 len이 count보다 크거나 같을 때 null 종료자를에 배치하여 출력을 자릅니다 buffer[count-1]. (...)

모든 함수 다른 보다 snprintfLEN = 카운트 경우 len 문자가 버퍼에 저장되며, 어떤 널 종결이 추가되지 않는다 (...)


22
Aslan의 이름으로 Microsoft 엔지니어 는 주요 안전 기능_snprintf 을 조용히 제거snprintf 하고 문자열이 null로 끝나지 않도록 허용하는 것을 도입했을 때 생각했던 것은 무엇입니까 ?!
Colin D Bennett

2
@ColinDBennett-그것은 이상하고 강력한 성 가시고 누군가가 전혀 생각한다면 나는 단서가 없습니다 :-)
Martin Ba

2
@MartinBa 예 죄송합니다. 제가 테스트 한 것은 template <size_t size> int _snprintf_s(char (&buffer)[size], size_t count, const char *format [, argument] ...);/ GS (보안 검사) 컴파일 플래그에서만 발생한다는 점도 언급해야합니다. 이 함수는 크기, 개수 및 길이를 알고 있습니다.
sekmet64 2014

3
mingw64는 달리 명시되지 않는 한 microsoft _snprintf 구현을 "일반"snprintf로 사용했습니다 (사용합니까?). nvd.nist.gov/vuln/detail/CVE-2018-1000101
domenukk

2
그것은 분노 (의 인정 하듯이 바보 (그리고 아마도 완전히 원본) 느낌표의 @Sajjon idioms.thefreedictionary.com/in+the+name+of+God ) 아마 약간으로 다진 맹세 ( en.wikipedia.org/wiki/Minced_oath는 ). 또 다른 예는 "제우스의 이름으로 ...?!"일 수 있습니다. ( forum.wordreference.com/threads/in-the-name-of-zeus.2132965 )
Colin D Bennett

19

snprintf (3) 맨 페이지에 따름.

함수 snprintf()vsnprintf()최대 size바이트 (후행 널 바이트 ( '\ 0') 포함)를 str.

예, 크기가 1보다 크면 종료 할 필요가 없습니다.


3
그리고 그것에 대해 신에게 감사합니다. 이것이 유일한 현명한 디자인입니다. 이러한 기능의 확인 된 버전의 요점은 안전하다는 것입니다. 모든 종료 malarkey를 손으로해야한다면 끔찍할 것입니다.
Kerrek SB

1
이것을 사용하기 전에 사용중인 플랫폼에서 테스트 해 보는 것이 좋습니다. 널 바이트를 작성 해야 하더라도 그렇지 않은 구현에 부딪혔다는 것을 알고 있습니다 (이전 MS 런타임을 사용하는 MinGW와 함께했을 수 있습니다).
Dmitri

10

버퍼 크기는 0, C 않는 표준에 따르면, vsnprintf()snprintf()널 (null)의 출력을 종료한다.

snprintf()함수에 상당한다 sprintf()버퍼의 크기 (S)로 지칭 상태 n 인수의 추가. n이 0이면 아무것도 기록되지 않고 s는 널 포인터가 될 수 있습니다. 그렇지 않으면 n-1st 이후의 출력 바이트는 배열에 기록되는 대신 폐기되고 실제로 배열에 기록 된 바이트의 끝에 널 바이트가 기록됩니다.

따라서 할당 할 버퍼의 크기를 알아야하는 경우 0 크기를 사용하고 널 포인터를 대상으로 사용할 수 있습니다. 내가 POSIX 페이지에 링크했지만, 이는 표준 C와 POSIX가 동일한 영역을 다루는 곳에서 차이가 발생하지 않는다고 명시 적으로 말합니다.

이 참조 페이지에 설명 된 기능은 ISO C 표준과 일치합니다. 여기에 설명 된 요구 사항과 ISO C 표준 간의 충돌은 의도하지 않은 것입니다. POSIX.1-2008의이 볼륨은 ISO C 표준을 따릅니다.

Microsoft 버전의 vsnprintf(). 버퍼에 충분한 공간이 없을 때 표준 C 버전과 확실히 다르게 작동합니다 (표준 함수가 필요한 길이를 반환하는 경우 -1을 반환합니다). 표준 C 버전이 수행하는 반면 Microsoft 버전 null이 오류 조건에서 출력을 종료한다는 것은 명확하지 않습니다.

TR 24731 안전 기능을 사용합니까?에 대한 답변도 참고하십시오 . ( 의 Microsoft 버전 은 MSDN 참조 vsprintf_s()) 및 안전하지 않은 C 표준 라이브러리 함수에 대한 안전한 대안은 Mac 솔루션입니까?


오, 사악하다, 그런 생각은 못 해. 반면에 ... :)
Prof. Falken

아, MS vsprintf ()가 저를 물었다 고 생각합니다. 그리고 저는 그것을 선택했습니다.-1 개의 습관
Prof. Falken 2011 년

4

일부 이전 버전의 SunOS는 snprintf로 이상한 일을했고 출력을 NUL로 종료하지 않았고 다른 사람들이하는 일과 일치하지 않는 반환 값을 가지고 있었을 수도 있지만 지난 10 년 동안 릴리스 된 모든 것은 C99를 수행했습니다. 말한다.


XP가 출시 된 지 10 년이 조금 넘었습니다. :-)
Prof. Falken

그리고 올해는 쓸모가 없었습니다. :)
Prof. Falken

4

모호성은 C 표준 자체에서 시작됩니다. C99와 C11은 모두 동일한 snprintf기능 설명을 갖습니다 . 다음은 C99의 설명입니다.

7.19.6.5 snprintf함수
개요
1 #include <stdio.h> int snprintf(char * restrict s, size_t n, const char * restrict format, ...);
설명
2이 snprintf함수는 fprintf출력이 s스트림이 아닌 배열 (인수로 지정됨)에 기록된다는 점을 제외하고 와 동일 합니다. 경우 nIS는 제로, 아무것도 기록되지 않고, s널 포인터가 될 수있다. 그렇지 않으면 n-1st를 초과하는 출력 문자 는 배열에 기록되지 않고 버려지고 실제로 배열에 기록 된 문자의 끝에 널 문자가 기록됩니다. 겹치는 객체간에 복사가 발생하면 동작이 정의되지 않습니다.
반환
3 snprintf함수를 작성했다되었을 것입니다 문자 수를 반환n종료 널 문자를 계산하지 않거나 인코딩 오류가 발생한 경우 음수 값을 계산하지 않고 충분히 커야합니다. 따라서 반환 된 값이 음수가 아니고보다 작은 경우에만 null로 끝나는 출력이 완전히 기록되었습니다 n.

한편 으로 문장

그렇지 않으면 st를 초과하는 출력 문자n-1 는 배열에 기록되지 않고 버려지고 실제로 배열에 기록문자의 끝에 널 문자가 기록됩니다.

있다고
합니다 (경우 s3 자 길이의 배열을 포인트 등) n3, 다음 2 개 문자가 기록되고, 2 일 이상 문자가 삭제됩니다 ; 그런 다음 널 문자는 그 2 다음에 기록됩니다 (그리고 널 문자는 기록 된 세 번째 문자가됩니다) .

그리고 이것은 원래의 질문에 대한 답이라고 믿습니다.
답변 :
겹치는 개체간에 복사가 발생하면 동작이 정의되지 않습니다.
경우 n0 아무 것도 출력에 기록되지이다
의 부호화 에러가 발생하지 않으면, 그렇지 않으면, 출력은 항상 널 종료 ( 의 여부에 관계없이 출력 배열 여부에 출력 끼워 일부 문자가 출력되도록 폐기하지 다음 경우 배열이 오버플로되지 않음)
그렇지 않으면 (인코딩 오류가 발생하는 경우) 출력 이 null로 종료되지 않은 상태로 유지 될 수 있습니다 .

반면에
마지막 문장

따라서 반환 된 값이 음이 아니고 다음보다 작은 경우에만 null로 끝나는 출력이 완전히 기록되었습니다. n

모호함을 제공 합니다 (또는 내 영어가 충분하지 않음). 나는 적어도 두 가지 방법으로이 문장을 해석 할 수 있습니다 :
1. 출력은 null로 끝나는 경우와 반환 값은 음이 아닌 및 경우에만 미만의n 반환 값이 경우 것이있는 수단 ( 하지 미만 n, 즉 출력합니다 (를 포함하여 종료 null 문자)가 배열에 맞지 않으면 출력 이 null로 종료되지 않습니다 ).
2. 반환 된 값이 음이 아니고 보다 작은 경우에만 출력이 완료됩니다 (문자가 삭제되지 않음) .n


나는 위의 해석 1이 답변과 모순되고 오해와 긴 논의를 야기한다고 생각합니다. 이것이 snprintf함수를 설명하는 마지막 문장이 모호함을 제거하기 위해 변경이 필요한 이유입니다 (C 언어 표준에 대한 제안서를 작성하는 근거를 제공합니다). 링크에 대한 @ "Martin Ba"덕분에 http://en.cppreference.com/w/c/io/fprintf (참조 )
에서 모호하지 않은 표현의 예를 가져올 수 있습니다 . 4)

" snprintf :이 기능의 설명을 변경하기위한 C 표준 제안 / 계획이 있습니까? " 질문도 참조하십시오 .


4
당신의 해석 1은 나에게 전혀 그럴듯하지 않은 것 같습니다. 나는 그 문장을 "(부수적으로, null로 끝나는) 출력이 완전히 작성 되었다면 ..."로 구문 분석합니다. 이것은 # 2로만 이해할 수 있습니다.
zwol

1
"널 종료 출력이 완전히 작성되었습니다"라는 문장의 부정은 "널 종료 출력이 완전히 작성 되지 않았습니다"입니다. 더 이상은 없습니다. 부정 된 문장 자체는 어떤 것이 쓰여졌다는 것을 의미하지 않습니다 (불완전한 널 종료 출력, 불완전한 널 종료 출력 또는 무색 녹색 아이디어 포함). 표준의 다른 위치에서는 출력이 불완전 할 때 정확히 기록되는 내용을 말하고, 그 위치 는 출력이 비어 있지 않으면 (n == 0) 출력이 null로 종료된다는 것을 나타냅니다.
n. '대명사'm.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.