MSVC 의 _vsnprintf 버그와 수정 방법

체스맨의 이미지

MSVC 6.0(서비스팩5 - 확실히 설치했었는지는 기억이 안나네요.)에서
_vsnprintf 함수가 vsnprintf 표준과 호환성을 유지하는데 일부 문제가
있는 것 같습니다.

1. 종료 문자를 감안해서 버퍼 길이를 입력해야 한다. 예를 들어 버퍼 길이가
len 이면, len-1 을 입력해줘야 한다.

2. 문자열이 종료 되지 않아도 음수값(실패)을 리턴하지 않는다. 예를 들어
입력된 버퍼길이가 len 일 때, 문자를 len 만큼 쓰고 len+1 에 문자열을
종료시켜야 하는데, 일단 len 만큼만 써지면 len 이 리턴된다.

다음과 같이 수정할 수 있습니다. 1은 간단히 해결되구요.
...
조금 더 생각을 해 봤습니다. 맨 아래 처음 올린 코드를 올려놨는데,
굳이 memset 할 필요 없이, 무조건 리턴값에 따라 문자열을 종료시키면
되는군요. vsnprintf 표준은 버퍼가 모자르면, 쓰는데까지 쓰고,
리턴값은 모두 성공적으로 쓸 수 있는데 필요한 버퍼 길이를 리턴하도록
돼 있는데, 지금으로서는 리턴값까지 동일하게 맞춰주긴 어려울 것 같네요.

#include <stdio.h>
#include <stdarg.h>
#include <memory.h>

int 
vsnprintf( char* buf, int len, const char* fmt, va_list arg ) 
{ 
	int result = _vsnprintf( buf, len-1, fmt, arg ); 
	if( result>=0 ) buf[result] = 0; 
	else buf[len-1]=0;
	return result; 
} 

int 
snprintf( char* buf, int len, const char* fmt, ... ) 
{ 
	int result; 
	va_list arg; 
	va_start(arg,fmt); 
	result = vsnprintf( buf, len, fmt, arg ); 
	va_end(arg); 
	return result; 
} 

=================================

다음은 처음 올렸던 memset 을 사용하는 좀 비효율적인 코드입니다.

2는 memset 으로 일단 버퍼를 0이 아닌 값으로 채워준 뒤,
_vsnprintf 를 호출한 후 문자열이 잘 종료됐는지 확인합니다.

#include <stdio.h>
#include <stdarg.h>
#include <memory.h>

int
vsnprintf( char* buf, int len, const char* fmt, va_list arg )
{
	int result;
	memset( buf, 0xff, len );
	result = _vsnprintf( buf, len-1, fmt, arg );
	if( result>=0 && buf[result] ) buf[result] = 0;
	return result;
}
Forums: 
체스맨의 이미지

여전히 리턴값을 vsnprintf 표준과 맞추진 못하지만, 다음과 같이 해서
버퍼 부족을 판단하는 코드가 동일해지도록 할 수 있습니다.

int 
vsnprintf( char* buf, int len, const char* fmt, va_list arg ) 
{ 
	int result = _vsnprintf( buf, len-1, fmt, arg );
	if( result>=0 ) {
		buf[result] = 0;
		return result;
	}
	buf[len-1]=0;
	return len;
}

다음과 같이 버퍼 부족을 판단하며 이것은 표준함수를 호출할 때와 동일하게
작성됩니다.

result = snprintf( buf, len, "%s\n", "Hello, world." );
if( result>=len ) {
	/* 버퍼 부족 */
}

Orion Project : http://orionids.org

체스맨의 이미지

다음은 MSVC 의 _vsnprintf 가 문제가 있는지 알아보는 예제입니다.

#include <stdio.h>

int
main( void )
{
	static char buf[] = "Hello, world\n";
	int result = _snprintf( buf, 5, "Hello" );
	if( result>0 )
		printf( "return value=%d\n", result );
	printf( buf );
	return 0;
}

문자열 길이가 5인 "Hello" 를 버퍼(길이 5)에 쓰면 오류를 내거나,
"Hell" 까지만 써져야 하는데, "Hello" 까지 쓰고, null terminate
되지 않아서 출력이 다음과 같이 나오면, 문제가 있는 것입니다.

Quote:

return value=5
Hello, world

Orion Project : http://orionids.org

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.