1단원 — 조건문
조건문은 프로그램의 **흐름을 가지가 갈리게** 합니다. 이 단원에서는 `if`/`else if`/`else`, 삼항 연산자, `switch`까지 분기 도구 세트를 익힙니다.
이 강의에서 배우는 것
- 1`if`/`else if`/`else`로 다단계 분기를 작성한다.
- 2삼항 연산자 `?:`를 짧은 조건 표현에 활용한다.
- 3`switch`/`case`/`break`/`default`의 동작을 정확히 안다.
- 4조건의 부울 결과(0/0 아님)를 이해한다.
왜 조건문이 필요한가? — "갈림길의 신호등"
지금까지의 프로그램은 위에서 아래로 한 번에 흘렀습니다. 하지만 현실 문제는 거의 항상 "어떤 경우엔 A, 다른 경우엔 B"의 모양입니다.
┌──────────┐
│ score≥90 │── Yes ──► A
└──────────┘
│ No
▼
┌──────────┐
│ score≥80 │── Yes ──► B
└──────────┘
│ No
▼
C 이하조건문은 이 갈림길의 **신호등**입니다.
핵심 개념
1) `if` / `else if` / `else`
if (score >= 90) printf("A\n");
else if (score >= 80) printf("B\n");
else if (score >= 70) printf("C\n");
else printf("D 이하\n");- 위에서 아래로 검사하다가 **처음 참인 가지**만 실행합니다.
- 그래서 `score >= 80`은 **"80 이상이면서 90 미만"** 의 의미가 자동으로 됩니다.
2) 삼항 연산자 `?:`
int max = (a > b) ? a : b;`if-else` 한 줄짜리 단순 분기에 적합합니다. 너무 길어지면 일반 `if`로.
3) `switch` / `case`
switch (op) {
case '+': result = a + b; break;
case '-': result = a - b; break;
default: printf("알 수 없는 연산자\n");
}흐름:
┌─────► case '+': ─► break ─► (스위치 종료)
op? ───┼─────► case '-': ─► break ─► (스위치 종료)
└─────► default ─► (스위치 종료)`break`가 없으면 다음 `case`로 **흘러내림**(fall-through)합니다. 의도한 게 아니면 잊지 말고 `break`.
4) 조건식의 진실값
C에서 **0은 false**, **0이 아닌 모든 값은 true**.
if (count) { ... } // count != 0 과 같음
if (ptr) { ... } // ptr != NULL 과 같음 (관용 표현)예제로 보기
예제 1 — `ex01_if.c` : 점수 → 등급
if (score >= 90) printf("등급: A\n");
else if (score >= 80) printf("등급: B\n");
else if (score >= 70) printf("등급: C\n");
else if (score >= 60) printf("등급: D\n");
else printf("등급: F\n");**입력**: `85`
점수 입력 (0~100): 등급: B**입력**: `59`
점수 입력 (0~100): 등급: F핵심: 가지가 위에서 평가되므로 **순서가 중요**합니다. `if (score >= 60)` 을 가장 위로 옮기면 90점도 D가 되어버립니다.
예제 2 — `ex02_ternary.c` : 삼항 연산자
int max = (a > b) ? a : b;
int min = (a < b) ? a : b;
const char *parity = (a % 2 == 0) ? "짝수" : "홀수";**입력**: `7 3`
두 정수 입력: 최댓값: 7
최솟값: 3
a는 홀수핵심: 삼항은 **값**을 만들기 때문에 변수 초기화에 바로 쓸 수 있습니다.
예제 3 — `ex03_switch.c` : 사칙연산 계산기
switch (op) {
case '+': result = a + b; break;
case '-': result = a - b; break;
case '*': result = a * b; break;
case '/': /* 0 검사 후 a/b */ break;
default: /* 오류 */
}**입력**: `12 + 5`
식 입력 (예: 3 + 4): 결과: 17.000**입력**: `10 / 0`
식 입력 (예: 3 + 4): 0으로 나눌 수 없습니다.핵심: `case` 라벨은 **정수형 상수**여야 합니다(문자도 정수). 문자열은 못 씁니다.
예제 4 — `ex04_nested_if.c` : 중첩 if (윤년 판정)
윤년 규칙: `4의 배수, 단 100의 배수는 제외, 400의 배수는 다시 포함`.
**입력**: `2024`
연도 입력: 2024년은 윤년입니다.**입력**: `2100`
연도 입력: 2100년은 윤년이 아닙니다.핵심: 중첩이 깊어지면 가독성이 급격히 떨어집니다. 한 번에 한 층까지가 좋습니다.
다른 시각으로 보기 — `if-else if`는 사실 중첩 `if`
if (score >= 90)
/* A */
else if (score >= 80)
/* B */
else
/* C 이하 */는 사실 다음과 똑같습니다.
if (score >= 90) {
/* A */
} else {
if (score >= 80) {
/* B */
} else {
/* C 이하 */
}
}`else if`는 가독성을 위한 **관용적 표기**일 뿐, 별도 키워드가 아닙니다.
자주 하는 실수
- **`==`와 `=` 혼동**: `if (x = 0)` 는 항상 false + 사이드 이펙트.
- **세미콜론 혼자**: `if (x > 0);` 처럼 세미콜론을 잘못 찍으면 빈 분기.
- **`break` 누락**: `switch`에서 의도치 않은 fall-through.
- **부동소수 동등 비교**: `if (a == 0.1)` 은 거의 항상 false. 오차를 허용해 비교(`fabs(a - 0.1) < 1e-9`).
- **`else`가 가까운 `if`에 묶임**: 중첩 시 중괄호 없이 쓰면 의도와 다른 `if`에 붙을 수 있습니다. **항상 중괄호**를 권장.
정리
- `if`/`else if`/`else`는 위에서 아래로 평가하므로 조건 순서가 중요.
- 삼항 `?:`는 한 줄 분기에서 변수 초기화에 유용.
- `switch`/`case`는 정수형(문자 포함) 분기에만 사용. `break` 잊지 말 것.
- 조건식에서 0은 거짓, 0이 아닌 모든 값은 참.
- 중첩이 깊어지면 함수로 분리하거나 가지를 평탄화하자.
직접 해 보기
cd src
gcc -std=c11 -Wall -o ex01 ex01_if.c && echo 85 | ./ex01
gcc -std=c11 -Wall -o ex02 ex02_ternary.c && echo "7 3" | ./ex02
gcc -std=c11 -Wall -o ex03 ex03_switch.c && echo "12 + 5" | ./ex03
gcc -std=c11 -Wall -o ex04 ex04_nested_if.c && echo 2000 | ./ex04응용:
- `ex03_switch.c`에서 `'+': result = a + b;` 의 `break`를 지워 보세요. 출력이 어떻게 달라지는지 관찰.
- `ex01_if.c`의 등급 분기 조건을 `<= 89`처럼 거꾸로 작성해 같은 결과를 내 보세요.
💻 예제 (examples)
실제로 컴파일·실행해 결과를 확인할 수 있는 예제입니다.
#include <stdio.h>
int main(void) {
int score;
printf("점수 입력 (0~100): ");
scanf("%d", &score);
if (score < 0 || score > 100) {
printf("범위를 벗어났습니다.\n");
return 1;
}
if (score >= 90) printf("등급: A\n");
else if (score >= 80) printf("등급: B\n");
else if (score >= 70) printf("등급: C\n");
else if (score >= 60) printf("등급: D\n");
else printf("등급: F\n");
return 0;
}
#include <stdio.h>
int main(void) {
int a, b;
printf("두 정수 입력: ");
scanf("%d %d", &a, &b);
int max = (a > b) ? a : b;
int min = (a < b) ? a : b;
const char *parity = (a % 2 == 0) ? "짝수" : "홀수";
printf("최댓값: %d\n", max);
printf("최솟값: %d\n", min);
printf("a는 %s\n", parity);
return 0;
}
#include <stdio.h>
int main(void) {
double a, b, result = 0;
char op;
printf("식 입력 (예: 3 + 4): ");
scanf("%lf %c %lf", &a, &op, &b);
int ok = 1;
switch (op) {
case '+': result = a + b; break;
case '-': result = a - b; break;
case '*': result = a * b; break;
case '/':
if (b == 0) { printf("0으로 나눌 수 없습니다.\n"); ok = 0; }
else result = a / b;
break;
default:
printf("지원하지 않는 연산자: %c\n", op);
ok = 0;
}
if (ok) printf("결과: %.3f\n", result);
return 0;
}
#include <stdio.h>
/* 윤년 판정: 4의 배수, 단 100의 배수는 제외, 400의 배수는 포함 */
int main(void) {
int year;
printf("연도 입력: ");
scanf("%d", &year);
int leap;
if (year % 4 == 0) {
if (year % 100 == 0) {
leap = (year % 400 == 0);
} else {
leap = 1;
}
} else {
leap = 0;
}
printf("%d년은 %s\n", year, leap ? "윤년입니다." : "윤년이 아닙니다.");
return 0;
}
📝 과제 (exercises)
직접 풀어보고, 막힐 때 정답을 펼쳐 비교해보세요.
문제 1 (hw01.c)
목표: 세 정수 a, b, c를 입력받아 **가장 큰 값**을 출력하세요.
- 파일명: hw01.c
▶정답 코드 펼치기 / 접기
#include <stdio.h>
int main(void) {
int a, b, c;
printf("세 정수 입력: ");
scanf("%d %d %d", &a, &b, &c);
int max = a;
if (b > max) max = b;
if (c > max) max = c;
printf("최댓값: %d\n", max);
return 0;
}
문제 2 (hw02.c)
목표: 사용자 나이를 입력받아 다음 메시지 중 하나를 출력하세요. - `0~7`: "유아" - `8~13`: "어린이" - `14~19`: "청소년" - `20~64`: "성인" - `65 이상`: "노인"
- 파일명: hw02.c
▶정답 코드 펼치기 / 접기
#include <stdio.h>
int main(void) {
int age;
printf("나이 입력: ");
scanf("%d", &age);
if (age < 0) printf("잘못된 입력\n");
else if (age <= 7) printf("유아\n");
else if (age <= 13) printf("어린이\n");
else if (age <= 19) printf("청소년\n");
else if (age <= 64) printf("성인\n");
else printf("노인\n");
return 0;
}
문제 3 (hw03.c)
목표: 1~12 사이의 월(month)을 입력받아 해당 월의 **일수**를 출력하세요. 2월은 28일로 단순 처리. `switch`-fallthrough를 활용해 보세요.
- 파일명: hw03.c
▶정답 코드 펼치기 / 접기
#include <stdio.h>
int main(void) {
int month;
printf("월(1~12) 입력: ");
scanf("%d", &month);
int days;
switch (month) {
case 1: case 3: case 5: case 7:
case 8: case 10: case 12:
days = 31; break;
case 4: case 6: case 9: case 11:
days = 30; break;
case 2:
days = 28; break;
default:
printf("잘못된 입력\n");
return 1;
}
printf("%d월은 %d일입니다.\n", month, days);
return 0;
}