scanf 문제 해결

salmon001의 이미지

scanf에 존재하는 수많은 문제(버퍼 오버플로우, stdin에 찌꺼기 남음 등)로 인해, 좀 더 안전하고 견고한 방법을 찾던 중 이 글을 발견했습니다.

http://www.giannistsakiris.com/2008/02/07/scanf-and-why-you-should-avoid-using-it/

감명깊게 읽고, 코드를 조금 수정해 보았습니다.

/* void safeFgets(char* str, int strlenSizePlus2, FILE* stream); 
* 입력 : fgets와 동일 
* 함수 설명 : fgets의 에러 처리, stdin에 남은 문자 발생 시 stdin 비워 줌. 
* 리턴 : 없음. 
*/ 
void safeFgets(char* str, int strlenSizePlus2, FILE* stream){ 
	if(!fgets(str, strlenSizePlus2, stream) && ferror(stream)){ 
		printf("fgets fail...\n"); 
		exit(EXIT_FAILURE); 
	} 
 
	/* '\0' 바로 앞 문자가 '\n'이 아니면 stdin에 문자가 남아있음을 의미 */ 
	if(str[strlen(str)-1] != '\n' && stream == stdin) 
		while(getchar() != '\n'); 
}
 
int main(void){
	char buffer[1024];
	int a;
 
	while(1){
		printf(“Input a number: ”);
		safeFgets(buffer, sizeof(buffer), stdin);
		if (sscanf(buffer, “%d”, &a) >= 1)
			break;
		else
			printf(“You didn't type a number\n”);
	}
 
	printf(“output: %d\n”, a);
 
	return 0;
}

혹시 이 코드에 잠재적인 문제 또는 개선할 점이 있다면 의견 주시면 감사하겠습니다.

읽어 주셔서 감사합니다.

shint의 이미지

----------------------------------------------------------------------------
젊음'은 모든것을 가능하게 만든다.

매일 1억명이 사용하는 프로그램을 함께 만들어보고 싶습니다.
정규 근로 시간을 지키는. 야근 없는 회사와 거래합니다.

각 분야별. 좋은 책'이나 사이트' 블로그' 링크 소개 받습니다. shintx@naver.com

salmon001의 이미지

그 함수를 쓰면 원하는 길이를 넘는 키 입력은 화면에 표시 안 되게도 할 수 있겠군요.
stdin에 문자가 남을 걱정도 안 해도 되겠네요.
다만 안타깝게도 저는 ansi C나 C99 문법만 쓰고 있어서(C++을 빨리 배워야 할 텐데요^^) 활용은 훗날이 되겠네요.
답변 감사합니다.

Anti-Lock의 이미지

버퍼보다 큰 라인을 읽을때 사용될법한 코드같은데...
그러면 함수명을 바꾸시는게 좋을듯합니다.
원래 fgets는 버퍼보다 큰 라인이 있어도
남아있는 부분을 버리지는 않습니다.
따라서 작성하신 함수는 fgets 함수를 대체할 수 없습니다.
호출자는 safe작동을 기대할 뿐인데,
읽히지못한 부분이 버려진다고 기대하진 않을테니까요.

stdin여부를 체크하는것은 사용자가 키보드로 입력하는 상황만을 가정하신듯 한데
파이프를 통한 입력도 가능합니다. 이런상황은 고려하셨나요?

익명 사용자의 이미지

초심자들은 입력버퍼 비우기에 지나치게 집착하는데,
입력버퍼를 비우는 것 자체가 표준입출력과는 잘 맞지 않는 행동입니다.
표준입출력 자체가 스크린과 키보드만을 가정한 것이 아니기 때문입니다.
파이프라인, 리다이렉션 등 여러가지 입출력방식들이 존재합니다.

그래서 fflush(stdin)이 표준이 아닌 것이고요.
(입력버퍼를 비운다는 행위 자체가 그 결과를 예상하기 힘든 행동임)

텍스트 환경에서의 대화식 입력이 필요하면
readline이라거나 ncurses라거나... 이미 만들어진 라이브러리를 활용하는 것이 바람직 합니다.

salmon001의 이미지

그렇다면 입력 버퍼를 비우는 행위는 하지 않는 것이 좋겠네요.

safeFgets는 폐기하기로 결정했습니다.

파이프라인, 리다이렉션, readline, ncurses는 오늘 처음 알았네요.

scanf의 오버플로우 문제를 fgets를 사용함으로써 해결했다는 것에 만족하겠습니다.

댓글 달기

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
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.