getchar()로 입력받을시 char c가 아닌 int c가 사용되는 이유는?

jungjury의 이미지

#include <stdio.h>

main() 
{
  int c;
  
  c = getchar();
  
  while ( c != EOF ){
        putchar(c);
        c=getchar();
        }
}

unsigned char c로 정의한 경우 EOF(=^Z)가 입력된 경우
무한루프에 빠지는데..

(signed) char c로 정의한 경우 나 int c로 정의한 경우에는
똑같이 ^Z입력시 루프가 종료됩니다...

반드시 int c로 getchar의 리턴값을 받아야 한다는
C programming FAQs의 내용(12.1)가 차이가 나는 것같아 헷갈립니다..

EOF가 integer로 정의 되어 있어서.. char를 써도 되지만...
비교시 형변환이 필요하기 때문에.. int를 권장하는 건가요?

Quote:

http://www.cinsk.org/cfaqs/html/node14.html#SECTION001410000000000000000

Q 12.1
이 코드에서 잘못된 부분이 있나요?

char c;
while ((c = getchar()) != EOF) ...

Answer
일단, getchar의 리턴 값을 저장하는 변수는 반드시 int이어야 합니다. getchar()는 어떠한 문자 값이나, EOF를 리턴할 수 있습니다. EOF는 int 타입이기 때문에 이 리턴 값을 char에 저장하는 것은 EOF를 잘못 해석하게 할 소지가 있습니다 (특히 char의 타입이 unsigned인 경우 문제가 심각합니다).

위의 코드처럼 getchar()의 리턴값을 char에 담을 경우, 두 가지 결과를 예상할 수 있습니다.

* char의 타입이 signed인 경우, 그리고 EOF가 -1로 정의된 경우, 문자 값이 부호 확장(sign extension)되어, 문자값 255가 (C 언어 표현으로 '\377' 또는 '\xff') EOF와 같아집니다. 따라서 입력 도중에 입력 처리가 끝나버릴 수 있습니다.

* char의 타입이 unsigned인 경우, EOF 값이 (상위 비트들이 잘라져 대개 255나 0xff의 값으로) 잘라져서, EOF로 인식이 되지 않아 버립니다. 따라서 이 경우, 무한 루프에 빠져 버립니다.12.3

이 버그는 char가 signed이고, 입력이 모두 7 비트 문자인 경우, 발견하기가 매우 힘듭니다 (char가 signed인지 unsigned인지는 implementation-defined입니다.)

lifthrasiir의 이미지

jungjury wrote:
#include <stdio.h>

main() 
{
  int c;
  
  c = getchar();
  
  while ( c != EOF ){
        putchar(c);
        c=getchar();
        }
}

unsigned char c로 정의한 경우 EOF(=^Z)가 입력된 경우
무한루프에 빠지는데..

(signed) char c로 정의한 경우 나 int c로 정의한 경우에는
똑같이 ^Z입력시 루프가 종료됩니다...

반드시 int c로 getchar의 리턴값을 받아야 한다는
C programming FAQs의 내용(12.1)가 차이가 나는 것같아 헷갈립니다..

EOF가 integer로 정의 되어 있어서.. char를 써도 되지만...
비교시 형변환이 필요하기 때문에.. int를 권장하는 건가요?

Quote:

http://www.cinsk.org/cfaqs/html/node14.html#SECTION001410000000000000000

Q 12.1
이 코드에서 잘못된 부분이 있나요?

char c;
while ((c = getchar()) != EOF) ...

Answer
일단, getchar의 리턴 값을 저장하는 변수는 반드시 int이어야 합니다. getchar()는 어떠한 문자 값이나, EOF를 리턴할 수 있습니다. EOF는 int 타입이기 때문에 이 리턴 값을 char에 저장하는 것은 EOF를 잘못 해석하게 할 소지가 있습니다 (특히 char의 타입이 unsigned인 경우 문제가 심각합니다).

위의 코드처럼 getchar()의 리턴값을 char에 담을 경우, 두 가지 결과를 예상할 수 있습니다.

* char의 타입이 signed인 경우, 그리고 EOF가 -1로 정의된 경우, 문자 값이 부호 확장(sign extension)되어, 문자값 255가 (C 언어 표현으로 '\377' 또는 '\xff') EOF와 같아집니다. 따라서 입력 도중에 입력 처리가 끝나버릴 수 있습니다.

* char의 타입이 unsigned인 경우, EOF 값이 (상위 비트들이 잘라져 대개 255나 0xff의 값으로) 잘라져서, EOF로 인식이 되지 않아 버립니다. 따라서 이 경우, 무한 루프에 빠져 버립니다.12.3

이 버그는 char가 signed이고, 입력이 모두 7 비트 문자인 경우, 발견하기가 매우 힘듭니다 (char가 signed인지 unsigned인지는 implementation-defined입니다.)

물론 저 코드는 구현체에 따라서 잘 작동하기도 합니다만 일반적으로는 버그에 해당한다고 볼 수 있습니다. EOF의 요구조건은 "0보다 작을 것"이며 보통 -1을 많이 쓰는데, 저 경우에는 char가 signed라서 getchar가 반환한 EOF가 signed char로 변환되어도 값이 보존되면서 비교에는 문제가 없기 때문입니다. 하지만 C FAQ에서도 언급했듯이 char가 signed인 unsigned는 구현체마다 다르므로 컴파일러에 따라서 문제가 생길 수도 있으므로 (unsigned라면 형변환되면서 값이 바뀌어버리겠죠) 가급적이면 쓰지 말아야 할 코드입니다. 항상 int로 쓰는 습관을 기르세요.

- 토끼군

eungkyu의 이미지

man page에 써있는 말입니다.

Quote:
fgetc(), getc() and getchar() return the character read as an unsigned char cast to an int or EOF on end of file or error.

음미해 보시면 답이 나올 것입니다.

eungkyu의 이미지

tokigun wrote:

물론 저 코드는 구현체에 따라서 잘 작동하기도 합니다만 일반적으로는 버그에 해당한다고 볼 수 있습니다. EOF의 요구조건은 "0보다 작을 것"이며 보통 -1을 많이 쓰는데, 저 경우에는 char가 signed라서 getchar가 반환한 EOF가 signed char로 변환되어도 값이 보존되면서 비교에는 문제가 없기 때문입니다. 하지만 C FAQ에서도 언급했듯이 char가 signed인 unsigned는 구현체마다 다르므로 컴파일러에 따라서 문제가 생길 수도 있으므로 (unsigned라면 형변환되면서 값이 바뀌어버리겠죠) 가급적이면 쓰지 말아야 할 코드입니다. 항상 int로 쓰는 습관을 기르세요.

- 토끼군

이 코드가 버그인 이유는 구현체마다 달라서 char가 signed인지 unsigned인지 모르기 때문이 아닙니다. 어느 경우에도 잘 작동하지 않는 코드이며, 명백한 버그 코드입니다.

getchar의 리턴값이 int인 이유는 fgetwc의 리턴값이 wchar_t가 아니고 wint_t이 이유와 동일합니다. 잘 생각해보세요~ :wink:

doldori의 이미지

eungkyu wrote:
이 코드가 버그인 이유는 구현체마다 달라서 char가 signed인지 unsigned인지 모르기 때문이 아닙니다. 어느 경우에도 잘 작동하지 않는 코드이며, 명백한 버그 코드입니다.

저는 토끼군님의 설명이 맞다고 생각하는데요. char형의 유부호 여부 이외에도
이식성 없는 형변환까지 관련된 문제이긴 합니다만. 많은 경우에 잘 작동하기
때문에 잘못 알고 있는 사람들이 많기도 하고요.
eungkyu의 이미지

많은 분이 잘못 생각하시고 있는 부분이 있는데, char ch = getchar();의 문제의 원인은 char가 signed인지 unsigned인지와 전혀 관련이 없습니다. 단지 문제의 결과 발생하는 현상이 char가 signed일 때와 unsigned일 때가 다를 뿐입니다.

자세히 설명하자면,

정상적인 경우:
맨페이지를 보면 getchar() 함수는 결과값을 unsigned char로 읽어서 int형으로 캐스팅한 결과를 리턴한다고 하고 있습니다. 결과를 제대로 int형으로 받으면 0 ~ 255의 정상적인 결과와 EOF라는 에러를 저장할 수 있습니다.

문제의 원인:
getchar() 함수는 257가지의 결과가 존재합니다. (256가지 정상적 결과 + EOF) 이 범위는 (signed이든 unsigned이든) char 형이 저장할 수 있는 256가지보다 크기 때문에 모든 결과를 받을 수가 없습니다. 즉, 어느 두 결과값은 겹칠 수 밖에 없습니다.

문제의 결과:
(EOF가 int형 -1이라 가정합시다)
char이 signed일 때와 unsigned일 때
공통점: 정상적인 결과값 255와 EOF를 구분할 수 없습니다.
차이점:

while ((ch = getchar()) != EOF)
    putchar(ch);
에서
ch가 signed일 때 - 정상적인 결과값인 255를 받았을 때 EOF를 받은 것과 같이 동작하는 문제가 있습니다.
-> signed char값에 255를 넣으면 -1이 되므로 식이 거짓이 됨
ch가 unsigned일 때 - EOF를 받았을 때 정상적인 결과값인 255를 받은 것과 갈이 동작하는 문제가 있습니다.
-> unsigned char값에 EOF를 넣으면 255가 되므로 식이 참이 됨

결론:
getchar()의 결과는 항상 int형으로 받아야 합니다. char형으로 받는건 언제나 잘못된 코드입이다. char로 받았는데도 잘 돌아갔다면, 다루는 파일에 우연하게도 255에 해당하는 값이 없었을 뿐이죠. 이식성 없는 형변환, char이 내부적으로 signed인지 unsigned인지는 관계가 없습니다.

정리가 되셨기 바랍니다.

doldori의 이미지

eungkyu wrote:
결론:
getchar()의 결과는 항상 int형으로 받아야 합니다. char형으로 받는건 언제나 잘못된 코드입이다. char로 받았는데도 잘 돌아갔다면, 다루는 파일에 우연하게도 255에 해당하는 값이 없었을 뿐이죠. 이식성 없는 형변환, char이 내부적으로 signed인지 unsigned인지는 관계가 없습니다.

네, 맞습니다. getchar()의 반환값을 char로 받아도 좋다고 한 사람은 아무도 없으니
안심하세요. :-) signed/unsigned 얘기가 나온 것도 그것이 원인이라는 뜻이
아니라 부호의 여부에 따라 오류가 발생할 수 있는 상황을 예로 든 것입니다. 잘못
알고 있는 사람이 많으니 FAQ에 올라온 것이고요.
아, 그리고 한 가지 덧붙이면 이식성 없는 형변환이란
eungkyu wrote:
-> signed char값에 255를 넣으면 -1이 되므로 식이 거짓이 됨

을 말한 것입니다.
eungkyu의 이미지

구현체에 따라 잘 동작할 수도 있다는 얘기에 좀 민감해진거 같네요.

우연히 잘 동작하는 경우가 있을 뿐 완전 잘못된 사용이라는 것이라는 것을 강조하고 싶었습니다 :)
("가급적 이렇게 사용하는게 좋습니다"가 아니구요 ^^)

댓글 달기

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