C provides no formal, built-in Boolean type. Boolean values are just integers
(though with greatly reduced range!), so they can be held in any integral type.
C interprets a zero value as “false” and any nonzero value as “true.”
The relational and logical operators, such as ==, !=, <
,
>=
, &&
, and ||
, return the value 1 for “true,” so
1 is slightly more distinguished as a truth value than the other nonzero
values (but see question
9.2).
C 언어는 C99 표준 다음부터 이제 boolean 타입을 지원하지만, 문제는 boolean
타입의 필요성에 따라, 표준으로 제정되기 이전부터 개발자들이 임의로 boolean
타입을 만들어 써 왔다는 것입니다. 따라서, 기존 프로그램들과 호환성을 위해서,
boolean 타입의 이름은 (이상하지만) _Bool
입니다. 좀 더 자세한 것은
질문
9.1을 보기 바랍니다.
#define
으로
정의를 하는 게 좋을까요, 아니면 enum을 쓰는 것이 좋을까요?
#define
과 enum 중 어느 것을 쓸 것인지는 그리 큰 문제가
되지 않습니다 (질문
2.22와
17.10을 참고하기 바랍니다.)
다음 둘 중 아무 것이나 써도 좋으며, 0과 1을 직접 써도 좋습니다:
#define TRUE 1 #define YES 1 #define FALSE 0 #define NO 0 enum bool {false, true}; enum bool {no, yes};그러나 일단 어떤 것을 쓰겠다고 정했다면, 한 프로그램이나 프로젝트 내에서는 계속 그것을 쓰는 것이 좋습니다. (그러나 디버거에서 변수의 값을 검사할때, 수치가 아닌 이름으로 보여 줄 수 있으므로 enum이 더 좋을 수도 있습니다. (Note 질문 2.22 참고)
다음과 같이 typedef를 쓸 수도 있습니다:
typedef int bool;또는
typedef char bool;또는
typedef enum { false, true } bool;
어떤 사람들은 다음과 같이 쓰는 것을 선호합니다:
#define TRUE (1==1) #define FALSE (!TRUE)또는 다음과 같이 “도우미(helper)” 매크로를 만듭니다:
#define Istrue(e) ((e) != 0)
그렇지만 이런 방식이 특별히 좋다는 것은 아닙니다. (질문 9.2를 참고하기 바랍니다; 질문 5.12와 10.2도 보시기 바랍니다).
_Bool
타입을 제공하기 때문에, 위 설명은 이제 올바른
답변이 아닙니다. C 언어는 boolean 타입을 제공하며, 타입 이름은
_Bool
입니다. 깔끔한 이름이 아닌, 이렇게 이상한 이름을 쓰는
이유는, 기존의 많은 C 프로그램들이, 위에서 설명한 것처럼 나름대로
boolean 타입을 이미 쓰고 있기 때문에, 이들 프로그램들과 호환을 위해서
이러한 이름을 쓰게 되었습니다. 표준 헤더 파일 <stdbool.h>
를
포함시키면, 매크로 bool9.2 이 _Bool
로 정의되어 있기 때문에,
다음과 같이 쓸 수 있습니다:
#include <stdbool.h> _Bool a; /* okay */ bool b; /* okay, using macro `bool' */또,
<stdbool.h>
는 매크로 true, false를
정의하고 있기 때문에 각각 1, 0 대신으로 쓸 수 있습니다.
Boolean 타입이 올 자리에 다른 정수 타입을 쓰게되면, 자동으로 변환되며, 아시다시피, 0이 아닌 모든 값은 1로, 0은 0으로 바뀝니다.
또한, C 언어가 boolean 타입을 지원한다고 해서, C++ 처럼,
비교 연산자들의 비교 결과가 _Bool
타입인
것은 아닙니다. 비교 연산자의 비교 결과는 여전히 int인 것을
주의하기 바랍니다. 여기에서 비교 연산자라고 하는 것은,
표준에서 relational operator와 equality operator를 포함한 것입니다.
즉, <
, <=
, >
, >=
, ==
,
!=
가 여기에 해당합니다.
#define
을 써서 TRUE를 1로 만드는 것은 위험하지 않을까요?
C 언어에서는 0이 아닌 비트가 하나라도 있으면 참(true)으로 인식될
테니까요. 만약에 어떤 함수나 연산자가 1이 아닌 다른 값을 리턴할 경우
어떻게 되는 거죠?
if ((a == b) == TRUE)그러나 위와 같이 직접 TRUE나 FALSE와 같은 지 비교하는 것은 무의미할 뿐더러, 매우 위험합니다. 왜냐하면 어떤 라이브러리 함수들은 (특히 isupper(), isalpha(), 등등) 참(true)을 나타내기 위해 `0이 아닌 값'을 씁니다. 즉, 이 때 `0이 아닌 값'은 꼭 1일 필요가 없습니다.
(또, 만약 여러분이 아래 코드보다
if (a == b)다음 코드가 더 향상된 것이라고 믿는다면,
if ((a == b) == TRUE)왜 다음과 같이 쓰지 않나요?
if (((a == b) == TRUE) == TRUE)아예 다음과 같이 쓰는게 더 좋은 것이라고 믿는 것인가요?
if ((((a == b) == TRUE) == TRUE) == TRUE)Lewis Caroll의 에세이, “What the Tortoise Said to Achilles.”를 읽어보시기 바랍니다.)
if (a == b)가 완벽하게 올바른 것과 마찬가지로, 아래 코드도 완벽히 올바른 코드입니다:
#include <ctype.h> ... if (isupper(c)) { ... }왜냐하면, isupper는, 참/거짓을, zero/nonzero로 알려주기 때문입니다. 비슷하게, 아래 코드는 좋은 코드입니다:
int isvegetable; /* really a bool */ ... if (isvegetable) { ... }마찬가지로, 아래 코드도 좋습니다:
extern int fileexists(char *); /* returns true/false */ ... if (fileexists(outfile)) { ... }위 두 예에서 isvegetable과 fileexists()는 개념상 boolean 타입입니다. 따라서 다음과 같이 쓰는 것은 전혀 개선이라고 할 수 없습니다:
if (isvegetable == TRUE)또는,
if (fileexists(outfile) == YES)(이러한 스타일이 좀 더 “안전한” 또는 “좋은 스타일”이라고 생각한다면, 정말로 위험하고 나쁜 스타일을 믿고 있다고 말할 수 있습니다. 가독성도 오히려 떨어집니다. 질문 17.10을 참고하기 바랍니다.)
여기에서 배울 가장 중요한 규칙은, 값을 대입할 때, 함수의 인자로 전달할 때, 또는 불리언 값을 리턴할 때에만 TRUE나 FALSE를 쓴다입니다. 즉, 비교할 때에 이런 것을 쓰는 것은 좋지 않습니다. (질문 5.3 참고)
TRUE나 FALSE와 같은 (또는 YES와 NO) 매크로를 쓰는 것은 어떻게 보면 좋은 것 같지만 C 언어에서 불리언 값이라는 개념이 헷갈리기 때문에, 어떤 프로그래머들은 0과 1이라는 수치를 직접 쓰는 것을 선호하기도 합니다. (질문 5.9를 참고하기 바랍니다.)
<stdbool.h>
을 통해서 매크로
true와 false를 제공하며, 각각 0과 1로 정의되어 있습니다.
질문
9.1에서 새 boolean type인 _Bool
을 참고하기 바랍니다.
if (p)
와 같은 코드를 쓰는 것은
나쁜가요?
어떤 사람들은 TRUE와 FALSE같은 심볼 이름을 쓰는 것은, 코드를 읽는 사람에게 boolean 값이 쓰인다는 것을 알려주는 역할을 하기 때문에, 좋다고 합니다. 또 어떤 사람들은 어차피 C 언어에서 boolean 값과 정의가 혼동스럽기 때문에, TRUE와 FALSE같은 매크로를 쓰는 것은 오히려 더 복잡하게 만든다고 생각합니다. (질문 5.9 참고)
Seong-Kook Shin