[CodeEater와 제로부터 시작하는 C언어] 7.1장 - 부울대수와 단축평가
부울 대수를 사용할 때 여러분이 결과적으로는 반드시 알아야할 것이 있다.
바로 단축평가(Short-Circuit Evaluation)라는 녀석이다.
놀라운 사실은 이 단축평가에 대해서 모르는 사람들도 꽤 많다는 것이다.
사실 고민했던게 이게 꼭 필수인가??
이게 좀 답하기는 애매한데 결론은 "지금은 몰라도 언젠간 반드시 알아야한다."
라는 것이다.
그래서 본문에 넣을까 부록으로 빼버릴까 고민을 하다가
본문에는 그래도 최대한 쉽고 필수적인것만 넣자고 해서 빼버렸다.
이번에는 이 단축평가에 대해서 알아보도록 하자.
우리는 이항 논리 연산자(AND, OR)을 굉장히 자주 사용한다
예컨데 아래와 같은 코드는 흔히 볼 수 있는 것이다.
#include <stdio.h>
int main() {
int a = 10;
if (1 <= a && a <= 10) {
printf("SUCCESS");
}
return 0;
}
위의 코드는 a가 1이상이면서 10이하일 때 SUCCESS라는 문자열을 출력하라는 코드이다.
이 코드에서는 우리가 AND연산자인 &&을 사용했다.
자, 여기서 우리가 이 AND연산자를 어떻게 참인지 판단하는가?
먼저 좌변의 조건이 참인지 확인하고 우변의 조건이 참인지 확인하게 된다.
그래서 둘다 참이면 참이라고 하였다.
그런데 만약, 그렇다면 좌변이 거짓이면 뒤를 확인할 필요가 있을까?
어짜피 뒤를 확인하나 안하나 거짓인게 확정되게 된다.
이 경우 우리는 어떻게 해야할까?
거짓인지 확정됫지만 그래도 우변을 확인해야할까?
이제 우리가 무슨 말을 할려는지 감이 올것이다.
AND연산이건 OR연산이건 좌변을 보고 판단이 이미 끝났을 경우,
C언어에서는 우변을 확인하지 않는다.
이를 프로그래밍 용어로 단축평가(Short-Circuit Evaluation)라고한다.
단축평가(Short-Circuit Evaluation) - 최종 평가를 진행 할때 부분평가로 이미 최종 평가가 결정되었다면 더 이상 평가를 진행하지 않는다.
이는 경우에 따라서 중요할 수도, 중요하지 않을 수도 있다.
왜냐하면 일반적으로 if문, for문 혹은 조건연산자, 비교연산자, 논리연산자 같이
참과 거짓과 관련된 부분에서는 "함수사용 혹은 변수의 증감식이 권장되지 않기"때문이다.
그래서 경우에 따라서 정말로 단축평가에 대해서 모르는 사람도 있다.
이 특성은 경우에 따라서 여러분의 코드가 의도한대로 동작하지 않는 원인이 되기도한다.
가령 아래의 상황을 보자.
#include <stdio.h>
#include <stdbool.h>
int main() {
int money = 0;
bool inMarket = false;
if (inMarket && ++money) {
printf("엄마한테 돈받아서 물건 사야지!\n");
} else {
printf("물건은 못사지만 엄마한테 돈이라도 받아야지!\n");
}
printf("현재 내가 가진 돈은 %d원이다.\n", money);
return 0;
}
상황을 가정해보자.
내가 매장안에 있으면 엄마한테 돈을 받아서 물건을 산다.
그리고 매장에 없어도 엄마한테 돈은 받는다.
이런 상황에서 위와 같은 코드는 여러분이 원하는 효과를 낼까?
애석하게도 여러분이 원하는대로 동작하지 않는다.
왜냐하면 단축평가가 이루어져서 inMarket이 true인지 false인지 확인을 하는데
inMarket이 true였다면 뒤를 확인해야할 의무가 생기지만
inMarket이 false였기에 뒤를 굳이 확인할 필요가 없기 때문이다.
그래서 money++이라는 구문은 동작하지 않는다.
그러면 저기 inMarket을 true로 교체해 보자.
#include <stdio.h>
#include <stdbool.h>
int main() {
int money = 0;
bool inMarket = true;
if (inMarket && ++money) {
printf("엄마한테 돈받아서 물건 사야지!\n");
} else {
printf("물건은 못사지만 엄마한테 돈이라도 받아야지!\n");
}
printf("현재 내가 가진 돈은 %d원이다.\n", money);
return 0;
}
이럴 경우에는 inMarket이 true더라도 &&의 특성상 반드시 뒤를 확인해야한다.
그래서 결과는 money++이 실행되게 된다.
이는 AND뿐만아니라 OR도 마찬가지이다.
다만 AND는 앞이 false면 더이상 가치판단을 하지 않지만,
반대로 OR은 앞이 true면 더이상 가치판단을 하지 않는다.
조금만 생각해보면 당연하긴 하다.
하지만 AND보다 OR이 더 쉴수하기는 쉽다.
가령 아래의 코드를 보자.
#include <stdio.h>
#include <stdbool.h>
int main() {
bool washing = false;
bool sleeping = false;
washing = !washing;
sleeping = !sleeping;
if (washing || sleeping) {
printf("자거나 씻거나 씻고 자거나!\n");
}
printf("씻기:%d, 자기:%d\n", washing, sleeping);
return 0;
}
여기서 여러분은 씻지 않았고 자지 않았다는걸 알 수 있다.
그래서 씻고나서 자려고한다.
이 경우 아무문제가 없다.
정상적으로 씻고나서 잠에 들고 printf는 출력될 것이다.
문제는 이를 좀 어떻게 깔끔하게 적어보겠다고 한줄에 적거나
if문에 넣거나 하면서 문제는 벌어진다.
#include <stdio.h>
#include <stdbool.h>
int main() {
bool washing = false;
bool sleeping = false;
if ((washing = !washing) || (sleeping = !sleeping)) {
printf("자거나 씻거나 씻고 자거나!\n");
}
printf("씻기:%d, 자기:%d\n", washing, sleeping);
return 0;
}
여기서 여러분은 씻고 잘것인가 씻기만 할것인가 자기만 할것인가?
그결과 여러분은 씻기만 하고 자지는 않을 것이다.
그 이유는 위에서 언급했지만 washing이 값이 반전되서 true가 되었고
OR은 앞이 true면 뒤는 확인하지 않기 때문이다.
이는 함수를 사용하는 경우에도 마찬가지이다.
#include <stdio.h>
#include <stdbool.h>
bool areYouHuman() {
printf("당신은 휴먼입니까?\n");
return true;
}
int main() {
if (true || areYouHuman()) {
}
if (false || areYouHuman()) {
}
return 0;
}
함수를 쓰면서도 이문제는 꽤 심각하게 일어난다.
위의 경우에서 areYouHuman은 무조건 true를 리턴하므로
사용자는 "이 if문은 무조건 동작할거야"라는 믿음을 준다.
근데 문제는 그걸 뛰어 넘어서 "이 함수는 무조건 동작할거야"
라는 그릇된 생각까지 번지는 경우가 있다.
그래서 if문에서는 사실 함수사용이나 변수의 변화식은 지양되는 편이다.
하지만 장점이 없는건 아니다. 식이 더 간결해 질 수 있고
단축평가에 익숙해지면 오히려 코드를 보는데 더 편해질 수 있다.
if (false || areYouHuman()) {
}
가령 위의 구문만 봐도 단축 평가에 익숙해지면
"앞조건이 참이면 뒤를 실행안하고 앞조건이 거짓이면 뒤를 실행하는군"
이라고 생각하게 된다는 것이다.
결론 - 단축평가를 이해하고 쓰자!
'제로부터 시작하는 프로그래밍 > C' 카테고리의 다른 글
[CodeEater와 제로부터 시작하는 C언어] 8장 - 조건문 (0) | 2020.06.12 |
---|---|
[CodeEater와 제로부터 시작하는 C언어] 7장 - 진리값과 부울대수 (0) | 2020.04.13 |
[CodeEater와 제로부터 시작하는 C언어] 6.1장 - 자료형끼리의 연산 (0) | 2020.04.13 |
[CodeEater와 제로부터 시작하는 C언어] 6장 - 연산자 (0) | 2019.12.08 |
[CodeEater와 제로부터 시작하는 C언어] 5.1장 - scanf (0) | 2019.12.03 |