[CodeEater와 제로부터 시작하는 C언어] 8장 - 조건문

누군가는 프로그래밍이라는 것은 조건과 반복이라고 한다.
우리가 컴퓨터를 쓰는 이유는 결국에는
"많은 작업들을 시키기 위해서"컴퓨터를 쓰는 것이다.
여기서 조건이라는게 뭘까?
사실 여러분은 앞에서 진리값과 논리연산, 비교연산, 조건연산을 배웠다.
그래서 이미 알고있는 개념이지만 좀 더 명확하게 이야기하도록해보면
경우에 따라 실행되는 로직이라고 볼 수 있다.

가령 위의 그림을 예로들면 조건에 따라 도서관에 갈수도 있고 PC방에 갈수도 있다.
시험기간이라면 도서관에 갈것이고 아니면 PC방에 갈 것이다.
이를 프로그래밍으로 할려면 어떻게 해야할까?
이때까지 우리가 배웠던 개념 들 중에서는 그 무엇도 이 문제를 해결할 수 없다.
그래서 해결하려면 사용하려면 새로운 개념이 필요하고 그게 바로 조건문이다.
조건문이 존재하기 때문에 우리는 특정 상황에서의 액션을 해결할 수 있다.
이를 뒤집어서 이야기하자면 특정 상황이 아닐 때의 액션역시 해결할 수 있다.

조건 문은 크게 보면 4가지의 종류가 있다.
사실 3가지는 함께 묶이고 나머지 하나는 좀 이질적이다.
여기서는 3가지 시리즈만 함께 소개하려고한다.
그래도 이름만 언급하자면 if, else, else if, switch가 있으며
여기서는 switch는 아직 다루지 않고 나머지 if와 else, else if만 다뤄붜자.

이번에 조건문(Conditional Statement)과 스코프(Scope), 섀도잉(Shadowing)에 대해서 사용할 것이다.
사실 스코프와 섀도잉은 자세히 이야기할 것은 아니다.
왜냐하면 공통 개념이기 때문이다.

조건 연산자라는 삼항연산자를 기억할 것이다.
조건 연산자((<조건>? <조건이 참일 때>: <조건이 거짓일 때>)) - 삼항 연산자, 조건부로 데이터를 출력
이때 잘보면 조건이 맞으면 ?뒤를, 조건이 틀리면 :뒤를 반환한다.
예시를 들자면 이런경우에 사용할 수 있다.
#include <stdio.h>
int main(){
int num;
scanf("%d", &num);
printf("num이 10보다 크면1, 아니면0\n",(10>num?1:0));
}
위의 코드를 num을 바꿔가면서 테스트 해보면 이제 조건연산자라는 뭔지 감이 잡힐것이다.
조건 연산자가 뭔지 알았으니 조건문이 뭔지는 더 정확하게 알 수 있다.
조건문은 조건 연산자가 코드블럭 단위로 바뀐것이다.
조건연산자는 특정 값만 조건을 정할 수 있으나
조건문은 값이 아닌 행위(코드)의 조건을 정할 수 있다.

if문은 위와같이 사용한다.
이제 여러분은 처음으로 main을 제외한 첫 블럭을 보게 된 것이다.
이렇게 {}안에 감싸지는 녀석을 C언어 및 다른 언어에서는 블럭(Block)이라고 부른다.
그리고 C언어에서의 블럭은 스코프(Scope)와 동일한 뜻이다.
이 블럭이라는 개념은 너무 중요하기 때문에 나중에 또 알려줄 것 이다.
지금은 간략하게 하고 넘어가도록 하자.
#include <stdio.h>
int main() {
int num = 5;
if (10 > num)
printf("10보다 %d가 작습니다.\n", num);
return 0;
}
if문의 양태는 저 블럭을 약식으로 쓰느냐 아니면 정식으로 쓰느냐 두가지로 나뉜다.
위의 예시는 블럭을 약식으로, 즉 명시적으로 블럭을 선언하지 않았다.
그렇기 때문에 위의 예제에서는 단 한줄밖에 적을 수 없다.
그럼 위의 예제가 어떻게 돌아갈지 생각해보자.
그냥 영어를 읽는다고 생각하고 읽으면
만약, 10이 num보다 크다면 이라고 읽으면 된다.
해당 조건이 true가 되면 아래 문장을 실행하게 된다.
if (10 > num)printf("10보다 %d가 작습니다.\n", num);
if (10 > num)
printf("10보다 %d가 작습니다.\n", num);
if (10 > num)
printf("10보다 %d가 작습니다.\n", num);
또한 if문의 경우 띄어쓰기 여부와는 상관이 없다.
사실 이는 if문의 성격이 아니라 C언어 자체의 특징이다.
C언어는 공백문자(엔터, 스패이스, 탭)가 한개 이상만 들어간다면 이는 모두 동일하게 간주한다.
가령 위의 3개의 예제는 모두 동등하다.

이러한 약식 조건문은 솔직히 말하면 좋지는 않다.
사람의 스타일에 따라 다르겠지만 보통은 한문장을 쓰더라도 정식을 쓰는게 낫다.
하지만 뭐 스타일 차이이니 여러분이 하고싶은데로 하면된다.
#include <stdio.h>
int main() {
int num = 5;
if (10 > num) {
printf("10보다 %d가 작습니다.\n", num);
printf("%d는 10보다 작습니다.\n", num);
}
return 0;
}
만약 두 문장이상을 쓰고싶다면 반드시 block을 명시적으로 선언해야한다.
만약 그렇지 않고 쓰면 어떻게 될까?
해봐라. 무슨말인지 알것이다.
물론 한가지의 액션만 취하려고 괄호를 사용할 수 있다.

이제 여러분은 if를 알게 됨으로써 융통성있는 코딩이 가능해졌다.
이는 이걸 보는 여러분의 프로그래밍 인생에 가장 큰 사건중 하나이다.
그럼 이제 이렇게 생각해보자.
조건이 맞는경우 A를 실행한다, 그런데 조건에 안맞으면 B를 실행한다.
이런 코딩은 도대체 어떻게 할 수 있을까?
우리가 알고있는 지식으로도 충분히 만들 수 있다.
바로 아래처럼 만들면 된다.
#include <stdio.h>
int main() {
int num = 5;
if (10 > num) {
printf("10보다 %d가 작습니다.\n", num);
}
if (!(10 > num)) {
printf("10보다 %d가 큽니다.\n", num);
}
return 0;
}
num을 바꾸면서 테스트해보자.
아니면 scanf로 num으 ㄹ받는것도 좋은 방법중 하나다.
사실 이렇게 사용해도 문제가 없지만
C언어에서는 이런 불쌍한 중생들을 구제하기위해서 획기적은 해결책을 내놨다.
이렇게 없어도 되지만 있으면 편한 문법들을 문법적 설탕(Syntax Sugar)라고 불린다.
그 대표적인 예제는 바로 else문이다.

이제 여러분들은 조건이 거짓일 때 더 심플하게 사용할 수 있는
else문에 대해서 알아보도록 해보자.

else문은 반드시 if문과 연합을 이루어야한다.
그리고 if문처럼 else도 괄호를 써도 되고 안써도된다.
다시 말하지만 이는 블럭들의 공통사항이다.
#include <stdio.h>
int main() {
int num = 5;
if (10 > num) {
printf("10보다 %d가 작습니다.\n", num);
}
else {
printf("10보다 %d가 큽니다.\n", num);
}
return 0;
}
위의 코드는 이제 else문을 써서 훨씬 깔끔하게 됬다.
사실 엄밀히 말하면 if를 반대조건으로 겹쳐쓰는거랑 if-else문은 조금 다르다.
그러나 여기서 언급하지는 않겠다.

우리는 if와 else라는 아주 좋고 유용한 무기를 얻었다.
하지만 이러한 단순한 조건만으로는 복잡한 갈래를 당연히 처리할 수 없다.
하지만 당연히 이를 해결할 수 있는 방법을 만들어 놨다.

바로 if와 else를 중첩해서 해결하는 것이다.
이를 이용해서 고차원적인 작업을 할 수 있다.
가령 if와 else를 중첩할수도 있다.
이를 중첩 조건문(Nested Conditional Statement)이라고 부른다.

사실 거창하게 제목까지 있고 중요하게 다루긴 하지만 특별한 문법이 있는건 아니다.
else는 당연히 필수가 아니며 마찬가지로 각각의 if와 else안에 추가적인 중첩을 할 수 있다.
문법적으로 중첩된 상태를 따로 지원하는건 아니지만 프로그래밍의 분기를 정하기 때문에
아주아주아주 중요한 개념중 하나이다.
주로 예시를 드는건 아주 고리타분하지만
달력 예제를 예시로 들 수 있다.
사골로 끓여서 이제 뼈도 안남았을만한 예제지만 아주 좋은 예제기도 하다.
흔히 2월달은 28일까지 존재한다. 하지만 윤년에는 29일까지 존재하게된다.
윤년은 보통 4의 배수로 떨어지는 해를 윤년이라고 한다.
하지만 이 중에서 특별하게 100으로 나누어떨어지는 해는 윤년이 아니다.
이렇게 윤년이 아닌 해를 평년이라고 한다.
즉 4의 배수지만 100의 배수는 아니어야 윤년이다.
그런데 또 여기서 100의 배수로 떨어지지만 400의 배수로 떨어지는 해는 윤년이다.
그러니까 4의 배수들은 윤년이지만, 그중에서 100의 배수는 평년이고,
또 그중에서 400의 배수는 윤년이다.
이를 프로그래밍으로 구현하는 예제는 학교에서 자주하는 예제이다.
그러나 아주 중요한 예제이고 이를 구현해보자.
아래 코드를 보기전에 혼자 생각해보고 구현해보는게 좋다.
#include <stdio.h>
#pragma warning(disable:4996)
int main() {
int num;
scanf("%d", &num);
if (num % 4 == 0) {
if (num % 100 == 0) {
if (num % 400 == 0) {
printf("윤년\n");
}
else {
printf("평년\n");
}
}
else {
printf("윤년\n");
}
}
else {
printf("평년\n");
}
return 0;
}
이번에는 scanf를 통해서 받아서 해보자.
여러분이 생각보다 이걸 만들기 힘들었을 것이다.
아니였으면말고.
보면 if와 else가 여러분 중첩하는 코드를 짜게된다.
이로 인해서 엄청나게 복잡한 코드를 짤 수 도있다.

하지만 이런 여러갈래의 문제를 반드시 중첩 반복문으로만 해결할 필요는 없다.
바로 else if라는 세번째 무기가 존재하기 때문이다.
바로 위의 코드를 else if로 고쳐보자.
#include <stdio.h>
#pragma warning(disable:4996)
int main() {
int num;
scanf("%d", &num);
if (num % 400 == 0) {
printf("윤년\n");
}
else if (num % 100 == 0) {
printf("평년\n");
}
else if (num % 4 == 0) {
printf("윤년\n");
}
else {
printf("평년\n");
}
return 0;
}
else if를 사용하면 비슷하게 만들 수 있다.
먼저 else if의 특성을 보자면
if->else if->else순으로 순서대로 확인을 한다.
그중에 위에가 하나라도 참이라면, 즉 if가 참이면 else if조건이 마찬가지로 참일지라도
확인하지 않는다.
이는 같은 else if끼리도 마찬가지다. 즉 위의 else if가 무조건 우선순위를 갖는다.
만약 거짓이라면 문제가 없지만 참이 되면 아래의 참여부는 확인하지 않는다.
많은 예제에서 중첩된 조건문을 해결하기 위해서 else if가 존재하는것 처럼 서술하는 경우가 많다.
실제로 그런 경우들도 있지만 사실 아닌 경우도 많다.
이는 여러분이 앞으로 프로그래밍 하면서도 알게될 것이기 떄문에
재미를 위해서 자세한 설명은 안하도록 하겠다.
아 물론 귀찮아서 안하는건 아니다.
'제로부터 시작하는 프로그래밍 > C' 카테고리의 다른 글
[CodeEater와 제로부터 시작하는 C언어] 7.1장 - 부울대수와 단축평가 (0) | 2020.04.14 |
---|---|
[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 |