연습문제들을 공부할 때 stdbool.h을 사용하는 경우가 있는데요.
이때 scanf의 서식지정자를 %d 로 주면 아래와 같은 에러 문구가 발생합니다.
1>c:\project\hello\hello\hello.c(11): warning C4477: 'scanf' : 서식 문자열 '%d'에 'int *' 형식의 인수가 필요하지만 variadic 인수 3의 형식이 'bool *'입니다.
컴파일은 되는 것 같은데 실행 시 true/false 또는 0/1 입력을 제대로 인식하지 못하는 것 같습니다. 제가 놓치고 있는 부분이 있는지 궁금합니다.
bool은 C99 표준안부터 추가되었습니다. stdbool.h 헤더 파일의 정의는 다음과 같습니다.
#define bool _Bool
#define false 0
#define true 1
C99부터 _Bool 자료형을 쓸 수 있고, stdbool.h를 선언하면 bool 자료형으로도 쓸 수 있습니다.
C99 이전에도 이미 많은 프로그래머들이 bool로 정의해서 사용하고 있었으므로 이전 코드와의 호환을 위해 _Bool로 만들었습니다.
정의를 보면 알겠지만 false는 0이고, true는 1입니다.
int scalar = 0;
bool b = (bool)scalar;
scalar는 0이고, bool 자료형으로 변환해도 0이 됩니다. 따라서 false입니다.
if( b == false ) ...
이런 코드가 가능합니다.
int scalar = 10;
bool b = (bool)scalar;
0이 아닌 다른 정수이면 1로 변환되고 true가 됩니다.
if( b == true ) ...
이런 코드가 가능합니다.
C99 이전에도 bool은 정수 타입이 사용되었습니다. 이런 이유로 C99 이후에 추가된 bool도 정수를 사용합니다. 표준안에 따르면 bool은 int로 자료형 승급(type promotion)이 발생합니다. 즉, 내부에서는 int로 처리되지만, 값은 0과 1만 가질 수 있습니다.
bool은 내부에서 int로 승급되므로 scanf에서 %d를 사용합니다. bool 타입을 위한 서식 지정자는 별도로 정의되어 있지 않습니다.
내부에서 int로 처리하므로 %d를 사용해야 하지만, 0 또는 1만 입력해야 합니다.
이러한 이유로 true와 false는 입력받을 수 없습니다.
bool 입력에는 %d를 사용해야 하며, 컴파일러는 경고를 표시하지만, 개발자는 이를 무시해야 합니다.
bool은 int로 자료형 승급이 이뤄지므로 0, 1이 아닌 다른 숫자도 입력받을 수 있습니다. 그러나 (bool)로 변환하면 0은 0, 0이 아닌 수는 모두 1로 변환됩니다. 0은 false, 1은 true입니다. 따라서
bool b = false;
scanf("%d", &b);
b = (bool)b; // 사용자가 0, 1이 아닌 숫자를 넣었을 때를 대비한 방어 코드
이와 같이 작성해야 안전합니다.
컴파일러 오류를 아예 없애고 싶다면 int로 선언하고, (bool)로 변환하는 방식으로 코드를 작성하면 됩니다.
int scalar = 0; bool b = false;
scanf("%d", &scalar);
b = (bool)scalar; // bool 변환, 0 -> 0, 0을 제외한 수 -> 1로 변환
bool 타입을 내부적으로 어떻게 처리할지는 C 언어 컴파일러(구현체)마다 다릅니다. 따라서 scanf에서 0, 1이 아닌 다른 숫자를 입력하는 것도 가능하고, 표준에는 강제 규정도 없습니다. 따라서 bool을 입력 받는다면 (bool)로 한 번 더 변환하는 게 안전합니다.
자바, 파이썬 같은 언어는 C 언어보다 20년 이상 늦게 탄생한 언어이므로 C 언어가 겪은 이런 문제를 지켜봤고, 처음부터 이를 보완하는 방향으로 언어가 설계되어서 이런 문제가 덜 발생합니다.
C 언어는 역사가 길어서 문법은 쉽지만, 정확하게 배우는 게 쉽지 않은 이유이기도 합니다.