[CodeEater와 제로부터 시작하는 C언어] 7장 - 진리값과 부울대수

4장 자료형에서도 짧막하게 진리값을 언급했고
6장 연산자에서 논리연산자와 비교연산자를 언급하면서 진리값을 또 언급했다.
하지만 기억에 잘 안날수도 있고 어려워서 이해못했을 수도 있다.
안다, 충분히 이해한다. 어짜피 여기서 다시 설명할거기 때문에 깊게 설명하지도 않았다.

부울 대수와 진리값이라는 것을 여러분은 알아야한다.
어찌보면 작은 부분이라서 짧게 언급하고 넘어가도 될것 같고
여러분이 고등학교때까지 배웠던 집합론과 아주 유사한부분이 많지만
그걸 프로그래밍에 맞게 다시 알고 가야한다.
쉬운부분이지만 중요한 부분이기 때문이다.

이번에는 진리값(Truth Value)와 부울 대수(Boolean Algebra)에 대해서 알게 될 것이다.
그리고 아래에는 진리값과 부울린이 혼용되서 사용되고 있는데 둘은 엄밀히 말하면 다르지만
같은 개념이라고 접근해도 크게 무리는 없다.
둘의 엄밀한 차이는 아래에 따로 언급되고 있으니 확인하면 좋을 것이다.

진리값은 두 종류의 값만 오로지 가진다. true와 false이며
프로그래밍 관습상 true는 1로 매핑되고 false는 0에 매핑된다.
하지만 우리가 진리값을 쓰고 싶다고해도 그냥 쓸 수 없다.
이 자료형은 과거에는 없고 비교적 최근(이라고 해도 좀 옜날이긴한데)에
추가되었기 때문에 그 흔적으로 우리가 이걸 사용하려면 추가적인 작업이 필요하다.

stdbool을 include 해줘야만 쓸 수 있다.

여러분이 예제를 하는데 stdbool.h를 include하지 않았는데도 되는 경우가 있다.
이는 여러분의 확장자를 잘확인해보자. 아마 cpp상태일 것이다.
c++에서는 bool이 기본형으로 들어가 있기 때문에 include안해도 사용가능하기 때문이다.
여튼 이녀석을 include하면 아래처럼 사용할 수 있다.
#include <stdio.h>
#include <stdbool.h>
int main() {
bool real = true;
bool fake = false;
printf("%d\n", real);
printf("%d\n", fake);
return 0;
}
이렇게 boolean 변수는 bool 지시자로 선언할 수 있으며
해당 값은 오로지 true와 false만 가질 수 있다.

출력을 해보면 true는 1로, false는 0이라는 걸 알 수 있다.
여기서 여러분이 착각해야할것은 boolean변수의 true, false와
진리값 참, 거짓은 조금은 다르다는 것이다.
과거에도 언급했지만 bool은 오로지 true와 false, 단 두가지, 즉 1과 0만 가질 수 있다.
하지만 진리값의 참과 거짓은 반드시 1과 0일 필요는 없다.
진리값에서 false는 0을 의미하고 true는 0을 제외한 모든 숫자를 의미한다.
가령 예를 들어보자.
#include <stdio.h>
#include <stdbool.h>
int main() {
printf("%d\n", 1 && -1);
return 0;
}
위 코드가 주어진다면 결과는 1(참)으로 나올것인가 거짓으로 나올 것인가?
여기서는 1도 참이고 -1도 참이므로 참이 된다.

즉 1도 참(true)이고 -1도 참(true)이라고 본것이다.
#include <stdio.h>
#include <stdbool.h>
int main() {
printf("%d\n", 1 && 0);
return 0;
}
그럼 이 코드는 어떠한가? 참인가?
우리가 0은 거짓으로 본다고했다.
그렇기 때문에 출력 결과 거짓으로 나타난다.

이는 실수를 비교해도 마찬가지이다.
그럼 bool에 정수를 매핑하면 어떻게 될까?
bool은 오직 true와 false만 가질 수 있다고 하였는데 정말 그럴까?
한번 해보자.
#include <stdio.h>
#include <stdbool.h>
int main() {
bool a = 1;
bool b = -1;
bool c = 3.14f;
bool d = 0;
printf("%d\n", a);
printf("%d\n", b);
printf("%d\n", c);
printf("%d\n", d);
return 0;
}
bool에 온갖 값들을 대입해보자.
그러면 결과가 어떻게 나올지 예측을 해보자.

뭐 의외라고 생각할 수도 있고 예상한 대로일 수도 있다.
bool은 오직 1과 0만 가진다.
그 중에서 false가 되는 조건은 오직 0뿐이며
나머지 값은 모두 true인 1로 강제 형변환된다.
a에서 1은 true이므로 당연히 a의 값은 true인 1이 된다.
b에서 -1은 0이 아니므로 true가 되어 b의 값은 true인 1이 된다.
c의 3.14는 0이 아니므로 true가 되어 1이 된다.
마지막 d는 0이므로 false가 된다.
#include <stdio.h>
#include <stdbool.h>
int main() {
bool tpt = true + true;
bool tmt = true - true;
bool tp10 = true + 10;
bool fm10 = false - 10;
printf("%d\n", tpt);
printf("%d\n", tmt);
printf("%d\n", tp10);
printf("%d\n", fm10);
return 0;
}
그러면 부울린의 연산은 어떻게 될까?
부울린은 true와 false라고 했다.
그래서 그냥 1과 0을 대체해서 계산하면된다.
위에서 부터 차례대로 생각해보면,
tpt는 2가 될것이고
tmt는 0이 될것이고
tp10은 11이 될것이고
fm10은 -10이 될 것이다.
하지만 bool이라는 변수는 1과 0밖에 못가진다는건 우리는 알고있다.

그래서 이 계산값을 bool이라는 값에 담게되는순간 0이면 그대로,
나머지는 1로 바뀌게 된다. 즉 다시 참과 거짓만 가지게 된다는 것이다.
이 특성은 아주 유용한 특성임과 동시에 여러 일어날 수 있는 부작용들을 해소하게 해준다.
그러한 부작용들에 대해 설명하려면 시간이 길어지므로 생략하도록 하겠다.
#include <stdio.h>
#include <stdbool.h>
int main() {
printf("%d\n", true + true);
printf("%d\n", true - true);
printf("%d\n", true + 10);
printf("%d\n", false - 10);
return 0;
}
물론 이는 변수에 담는 행위를 했을때의 이야기고
위처럼 그냥 연산하면 사정은 좀 달라진다.
부울린과의 연산은 무조건 정수로 나오게되는데
여튼 여기에 대해서 설명하려면 조금 복잡하다.

왜 이렇게 되는지 정확하게 알고 싶다면 부록6.1의 정수 승격부분을 확인하도록하자.

이제 부울 대수와 진리표에 대해서 알아보자
부울 대수는 깊게 알아보려면 굉장히 복잡하기 때문에 자세한건 이야기 하지 않겠다.
간단히 이야기하면 프로그래밍을 할때 참과 거짓을 판별하는 것을 부울대수라고한다.
그냥 뭐 진리값과 동치라고 생각해도 무방하다.
가령 노력하면서 똑똑하면 성공한다라는 명제가 존재한다고 해보자.
이 경우 (노력 AND 똑똑 = 성공)이라는 부울 대수식이 성립한다.
이 때 노력만 해서도 성공할 수 없고 똑똑하기만 해서도 성공할수 없다.
노력하면서 똑똑해야만 성공할 수 있다.
만약 노력하거나 똑똑하거나하면 성공할수 있다라는 명제라면
(노력 OR 똑똑 = 성공)이라고 하면된다.
이 경우 둘중 하나만 되면 성공하는 것이다.
우리가 자주쓰는 부울 연산은 총 7가지가 존재하며 이를 알아보자.
1. NOT - true를 false로, false를 true로 바꾼다
2. AND - 양 값이 둘다 참이어야만 참이고 나머지는 거짓
3. OR - 양 값중 하나만 참이어여도 참, 둘다 거짓이면 거짓
4. XOR - 양 값이 서로 달라야 참, 서로 같으면 거짓
5. NAND - AND의 반전
6. NOR - OR의 반전
7. XNOR - XOR의 반전
5,6,7은 2,3,4의 각각 반전이므로 1,2,3,4만 알아보자.

유일하게 양값을 비교하는게 아니라 자신값만 비교하는 단항연산자이다.
그냥 진리값을 반전시키면된다. 간단하지만 강력하다.

특정 명제가 참이 되기위해서는 두개의 조건이 모두 참이 되어야한다.
"대한민국 국민이면서 남자면 군대를 가야한다"라는 명제가 있다면 이는 AND연산으로 묶여있다고 불 수 있다.

특정 명제가 참이 되기위해서는 두개의 조건 중 하나만 참이 되면된다.
"신분증이 있거나 여권이 있으면 신원을 확인할 수 있다"라는 명제가 있다면
이는 OR연산으로 묶여있다고 볼 수 있다.
물론 둘다 참이여도 참이다.

OR중에서 조금 특이한 상황인데 둘중 하나만 참이어야 하는 상황이다.
예를들어 현재 내가 1000원이 있고 소고기와 닭고기, 가스버너가 각각 500원이라고 가정해보자.
이 때 "닭고기만 사거나 소고기만 사야 고기를 먹을 수 있다"라는 명제가 존재한다면
이는 XOR연산으로 묶여있다고 볼 수 있다.
만약 소고기와 닭고기를 동시에 사면 가스버너를 못사서 고기를 어짜피 못먹게 되기 때문이다.
아니면 날걸로 먹던가.
#include <stdio.h>
#include <stdbool.h>
int main() {
bool hasGirlFriend = false;
bool hasMoney, isHandsome;
printf("1이면 여자친구 있는 거임\n0이면 없음\n\n");
hasMoney = true;
isHandsome = false;
hasGirlFriend = hasMoney || isHandsome;
printf("돈이 있지만 못생길 경우 : %d\n",hasGirlFriend);
hasMoney = false;
isHandsome = true;
hasGirlFriend = hasMoney || isHandsome;
printf("잘생겼지만 돈이 없는 경우 : %d\n", hasGirlFriend);
hasMoney = false;
isHandsome = false;
hasGirlFriend = hasMoney || isHandsome;
printf("못생기고 돈이 없는 경우 : %d\n", hasGirlFriend);
return 0;
}
진리값들은 이런식으로 사용할 수 있다. 위는 OR연산을 사용한 예제이다.

C언어에서 논리연산의 경우 XOR을 제외한 NOT과 AND, OR을 제공한다.
비트 단위에서는 XOR까지 제공하지만 논리연산에서는 제공하지 않는다.
만약 XOR이 필요하다면 직접 만들어서 써야한다.
#include <stdio.h>
#include <stdbool.h>
int main() {
bool a = true;
bool b = true;
bool result = (a != b) && (a || b);
printf("%d\n", result);
return 0;
}
result를 보면 어떻게 결과가 나오는지 확인할 수 있다.
a와 b를 바꿔서 테스트 하면서 여러분이 원하는 결과가 나오는지 확인해보자.
(a != b) && (a || b)
결국 둘이 다른 경우에만 OR연산을 수행한다면 이는 XOR연산이라고 할 수 있다.

진리값과 부울 대수를 배우면서 여러분이 알아야할 연산자는 논리 연산자 뿐만이 아니다.
여러분은 비교연산자도 알아야한다.
비교연산자와 논리연산자는 연산자들 중에서
결과값이 유일하게 진리값으로 나오는 연산자들이기 때문이다.
사실 뭐 그리 중요하지 않을 수 있다.
직관적으로 해결되기 때문이다.
하지만 뭐든지 알고 쓰는게 중요하다고 생각한다.
이번에도 비교적 짧게 혹은 비교적 자세하게 이야기 해봤지만
당연히 첫술에 배부를리 없고 앞으로 더 확인해봐야할게 많을 것이다.
일단 이정도를 알고 있으면 조건문을 배울 때 이제 많이 도움이 될 것이다.
'제로부터 시작하는 프로그래밍 > C' 카테고리의 다른 글
[CodeEater와 제로부터 시작하는 C언어] 8장 - 조건문 (0) | 2020.06.12 |
---|---|
[CodeEater와 제로부터 시작하는 C언어] 7.1장 - 부울대수와 단축평가 (0) | 2020.04.14 |
[CodeEater와 제로부터 시작하는 C언어] 6.1장 - 자료형끼리의 연산 (0) | 2020.04.13 |
[CodeEater와 제로부터 시작하는 C언어] 6장 - 연산자 (0) | 2019.12.08 |
[CodeEater와 제로부터 시작하는 C언어] 5.1장 - scanf (0) | 2019.12.03 |