조건문 if, else if, else
if·else if·else가 bool 조건에 따라 실행 경로를 고르는 방식을 익히고 중첩 조건을 명확하게 구성합니다.
지금까지의 프로그램은 위에서 아래로 모든 문장을 차례로 실행했습니다. 하지만 점수가 60점 이상일 때만 합격을 알리거나, 잔액이 충분할 때만 출금을 처리하려면 조건에 따라 실행할 문장을 골라야 합니다. 이렇게 실행 경로를 고르는 문장을 조건문(conditional statement)이라고 하며, C++에서 가장 기본이 되는 것이 if 문입니다.
이 글은 if, if-else, else if 체인을 차례로 보고, 중첩된 if에서 else가 어디에 붙는지, C++17의 초기화 문이 있는 if, 조건 순서와 경계값(boundary value)을 함께 점검하는 방법을 다룹니다. 조건식은 3장에서 본 관계 연산자와 논리 연산자로 만듭니다.
조건은 bool로 해석됩니다
if의 괄호 안 식은 문맥상 bool로 변환(contextual conversion to bool)된 뒤, 그 값이 true이면 딸린 문장을 실행하고 false이면 건너뜁니다. 관계 연산자와 논리 연산자는 결과가 이미 bool이므로 그대로 쓰입니다.
bool이 아닌 값도 조건이 될 수 있습니다. 정수와 실수는 0이면 false, 0이 아니면 true이고, 포인터는 널 포인터이면 false입니다. 그래서 if (count)는 if (count != 0)과 같은 뜻이며, 8장에서 볼 if (std::cin >> x)처럼 스트림이 읽기에 성공했는지를 조건으로 쓰는 코드도 이 변환에 기댑니다. 다만 무엇을 비교하는지 드러나도록, 숫자 조건은 처음에는 count != 0처럼 비교를 직접 쓰는 편이 읽기 쉽습니다.
if 문
if 문은 조건이 참일 때만 딸린 문장을 실행합니다. 딸린 문장은 문장 하나이며, 여러 문장을 실행하려면 중괄호로 묶은 복합문(compound statement)을 씁니다.
#include <iostream>
int main() {
int score{75};
if (score >= 60) {
std::cout << "pass\n";
}
int temperature{20};
if (temperature < 0) {
std::cout << "freezing\n";
}
std::cout << "done\n";
}pass
donescore >= 60은 75가 60 이상이어서 참이므로 pass를 출력합니다. temperature < 0은 거짓이어서 freezing 블록을 건너뛰고, if 문 다음의 문장이 이어서 실행됩니다. 조건이 거짓일 때 아무것도 하지 않아도 되는 경우에는 이처럼 if만 씁니다.
if-else 문
조건이 참일 때와 거짓일 때 서로 다른 일을 해야 하면 else를 붙입니다. 두 블록 가운데 정확히 하나만 실행됩니다.
#include <iostream>
int main() {
double balance{500.0};
double amount{700.0};
if (amount <= balance) {
balance -= amount;
std::cout << "withdrawn, balance " << balance << '\n';
} else {
std::cout << "insufficient, balance " << balance << '\n';
}
}insufficient, balance 500출금액 700이 잔액 500보다 커서 조건이 거짓이므로 else 블록이 실행되고, 잔액은 바뀌지 않습니다. double 값 500.0은 기본 출력 형식에서 소수부가 없으면 500으로 출력됩니다. 수수료나 동시 출금 같은 실제 은행 규칙은 빼고, 잔액 하나를 비교하는 단순한 예입니다.
else if 체인
세 가지 이상의 경우를 나눌 때는 else 뒤에 다시 if를 이어 붙입니다. 이것은 새로운 문법이 아니라, else의 딸린 문장이 또 하나의 if 문인 모양을 줄을 맞춰 쓴 것입니다.
#include <iostream>
int main() {
int grade{88};
if (grade >= 90) {
std::cout << "grade A\n";
} else if (grade >= 80) {
std::cout << "grade B\n";
} else if (grade >= 70) {
std::cout << "grade C\n";
} else if (grade >= 60) {
std::cout << "grade D\n";
} else {
std::cout << "grade F\n";
}
std::cout << "end of chain\n";
}grade B
end of chain체인은 위에서부터 조건을 검사하고, 처음으로 참이 된 조건의 블록 하나만 실행한 뒤 체인 전체를 빠져나갑니다. 아래 그림처럼 88은 grade >= 90에서 거짓이어서 다음 조건으로 내려가고, grade >= 80에서 참이 되어 B 블록을 실행합니다. 그 아래의 grade >= 70과 grade >= 60도 88에 대해서는 참이지만, 이미 블록을 골랐으므로 검사하지 않습니다.
else if 체인은 위에서 내려가 첫 true에서 멈춘다
grade=88에서 >=90 거짓, >=80 참, B 실행 후 나머지 분기를 건너뛰는 실제 조건 평가 경로이다.
IF / ELSE IF / ELSE
if 체인은 첫 true의 블록 하나를 실행한다
각 조건은 bool로 해석되며, 위에서 내려가 첫 번째 true의 블록 하나만 실행합니다.
grade >= 90은 falseA 블록을 건너뛰고 다음
else if조건으로 내려갑니다.grade >= 80은 trueB 블록만 실행하고 이 체인의 조건 검사를 끝냅니다.
첫 true를 찾음
선택된 블록을 실행하고 아래 else if와 else를 건너뜁니다.
아직 모두 false
다음 조건을 계속 검사하며, 끝까지 true가 없으면 존재하는 else를 실행합니다.
bool을 만들고, 그 밖의 허용되는 조건 표현식도 문맥상 bool로 변환된 뒤 분기를 결정합니다.이 예의 조건
- 본문과 원 도표에 제시된 사례의 입력·조건을 사용합니다. 설명용 수치나 독립 비교 경로는 각 단계에 표시합니다.
- 현재 상태·값과 전환 근거의 위치는 모든 단계에서 같습니다. 조건이 맞지 않거나 실행되지 않은 경로를 성공한 동작으로 해석하지 않습니다.
관계도의 보충 설명
| 범위 | 원문 |
|---|---|
| 도표 제목 | 점수 88이 첫 참인 B에서 선택 체인을 끝낸다 |
| 관계·조건 설명 | 데스크톱과 같은 입력, 두 조건, A와 B 실행, 다음 조건, 나머지 분기 생략의 일곱 노드와 여섯 간선이다. 90 조건은 거짓이라 80 조건으로, 80 조건은 참이라 B로 간 뒤 아래 분기를 생략한다. A의 참 경로도 그 블록에서 체인을 끝낸다. 모든 조건을 검사하고 여러 블록을 실행하는 세 독립 if가 아니다. |
| 관찰·상황 | 값·보충 정보 | 변화·이유 |
|---|---|---|
| 첫 조건 grade=88을 첫 조건의 기준값 90과 비교한다. |
|
|
| 둘째 조건 80 이상인지 확인한다. |
|
|
| 선택된 출력 B를 처리하고 남은 갈래를 건너뛴다. |
|
|
else if 체인은 위에서 내려가 첫 true에서 멈춘다
grade=88에서 >=90 거짓, >=80 참, B 실행 후 나머지 분기를 건너뛰는 실제 조건 평가 경로이다.
IF / ELSE IF / ELSE
if 체인은 첫 true의 블록 하나를 실행한다
각 조건은 bool로 해석되며, 위에서 내려가 첫 번째 true의 블록 하나만 실행합니다.
grade >= 90은 falseA 블록을 건너뛰고 다음
else if조건으로 내려갑니다.grade >= 80은 trueB 블록만 실행하고 이 체인의 조건 검사를 끝냅니다.
첫 true를 찾음
선택된 블록을 실행하고 아래 else if와 else를 건너뜁니다.
아직 모두 false
다음 조건을 계속 검사하며, 끝까지 true가 없으면 존재하는 else를 실행합니다.
bool을 만들고, 그 밖의 허용되는 조건 표현식도 문맥상 bool로 변환된 뒤 분기를 결정합니다.이 예의 조건
- 본문과 원 도표에 제시된 사례의 입력·조건을 사용합니다. 설명용 수치나 독립 비교 경로는 각 단계에 표시합니다.
- 현재 상태·값과 전환 근거의 위치는 모든 단계에서 같습니다. 조건이 맞지 않거나 실행되지 않은 경로를 성공한 동작으로 해석하지 않습니다.
관계도의 보충 설명
| 범위 | 원문 |
|---|---|
| 도표 제목 | 점수 88이 첫 참인 B에서 선택 체인을 끝낸다 |
| 관계·조건 설명 | 데스크톱과 같은 입력, 두 조건, A와 B 실행, 다음 조건, 나머지 분기 생략의 일곱 노드와 여섯 간선이다. 90 조건은 거짓이라 80 조건으로, 80 조건은 참이라 B로 간 뒤 아래 분기를 생략한다. A의 참 경로도 그 블록에서 체인을 끝낸다. 모든 조건을 검사하고 여러 블록을 실행하는 세 독립 if가 아니다. |
| 관찰·상황 | 값·보충 정보 | 변화·이유 |
|---|---|---|
| 첫 조건 grade=88을 첫 조건의 기준값 90과 비교한다. |
|
|
| 둘째 조건 80 이상인지 확인한다. |
|
|
| 선택된 출력 B를 처리하고 남은 갈래를 건너뛴다. |
|
|
마지막 else는 선택 사항이며, 모든 조건이 거짓일 때 할 기본 동작이 있을 때만 둡니다. else가 없고 어떤 조건도 참이 아니면 체인은 아무 블록도 실행하지 않습니다.
중첩 if와 else의 짝
if 블록 안에 다시 if 문을 둘 수 있습니다. 다음 예는 관리자인지 먼저 확인하고, 관리자일 때만 등급을 다시 확인합니다.
#include <iostream>
int main() {
bool isAdmin{true};
int level{5};
if (isAdmin) {
if (level >= 5) {
std::cout << "super admin\n";
} else {
std::cout << "admin\n";
}
} else {
std::cout << "user\n";
}
}super admin같은 판단은 if (isAdmin && level >= 5), else if (isAdmin), else의 체인으로도 쓸 수 있으며, 세 경우 모두 같은 출력을 냅니다. 중첩이 두세 단계를 넘어가면 이처럼 조건을 &&로 묶거나, 판단 부분을 함수로 나누어 각 분기가 무엇을 뜻하는지 드러나게 합니다.
중괄호 없이 if를 중첩하면 else가 어느 if의 짝인지 헷갈릴 수 있습니다. C++에서 else는 들여쓰기와 상관없이 아직 else를 갖지 않은 가장 가까운 if와 짝을 이룹니다.
if (a > 0)
if (b > 0)
std::cout << "both positive\n";
else
std::cout << "a is not positive\n";들여쓰기는 else가 바깥 if (a > 0)의 짝인 것처럼 보이지만, 실제로는 안쪽 if (b > 0)의 짝입니다. 그래서 a가 -1이면 아무것도 출력하지 않고, a가 1이고 b가 -1이면 사실과 다른 a is not positive를 출력합니다. GCC는 -Wall에 포함된 -Wparentheses가 켜는 -Wdangling-else로 이런 코드를 경고하며, 바깥 if에 중괄호를 두르면 의도한 짝이 됩니다.
중괄호와 문장의 범위
딸린 문장이 하나뿐이면 중괄호를 생략할 수 있습니다. 문장 하나는 여러 줄에 걸칠 수도 있으므로, 생략할 수 있는지는 줄 수가 아니라 문장 수로 판단합니다.
if (score >= 60)
std::cout << "pass\n";
std::cout << "congratulations\n";두 번째 출력문은 들여쓰기 때문에 if에 속한 것처럼 보이지만, 실제로는 if 문이 끝난 뒤의 문장이어서 점수와 상관없이 항상 실행됩니다. 중괄호 없이 쓴 코드에 나중에 문장을 덧붙이면 이런 실수가 생기기 쉽습니다. GCC의 -Wmisleading-indentation은 C++에서 -Wall로 켜지며 이런 들여쓰기를 경고하지만, 처음부터 모든 분기에 중괄호를 쓰면 블록의 범위가 코드에 그대로 드러납니다.
조건 자리에 == 대신 =를 쓰는 실수도 자주 나옵니다. if (x = 0)은 비교가 아니라 x에 0을 대입하고, 대입식의 값 0이 false로 해석되어 블록이 절대 실행되지 않습니다. 반대로 if (x = 5)는 항상 참입니다. GCC는 -Wparentheses로 진리값 자리의 대입을 경고하므로, -Wall을 켜고 경고를 읽는 습관이 도움이 됩니다.
초기화 문이 있는 if
C++17부터는 조건 앞에 초기화 문을 둘 수 있습니다. 형식은 if (초기화문; 조건)이며, 초기화 문에서 선언한 변수는 조건과 if 블록, else 블록 안에서만 쓸 수 있습니다.
#include <iostream>
#include <string>
int main() {
std::string word{"compiler"};
if (auto n = word.size(); n > 5) {
std::cout << "long word: " << n << '\n';
} else {
std::cout << "short word: " << n << '\n';
}
// 여기서는 n을 쓸 수 없습니다.
}long word: 8"compiler"는 8글자이므로 n은 8이고, n > 5가 참이어서 첫 블록이 실행됩니다. 조건 판단에만 필요한 변수를 if 바깥에 선언하면 그 변수가 함수 끝까지 남아 다른 코드에서 잘못 쓰일 수 있습니다. 초기화 문을 쓰면 변수의 범위가 판단에 필요한 곳으로 좁아집니다.
조건 순서와 경계값
else if 체인은 처음 참인 조건에서 멈추므로, 범위가 겹치는 조건은 좁은 조건부터 놓아야 합니다. 학점 체인에서 grade >= 60을 맨 위에 두면 90점도 그 블록에서 멈추어, 아래의 grade >= 90에는 도달하지 못합니다.
체인을 점검할 때는 대푯값 하나보다 각 기준값과 그 바로 아래 값을 넣어 보는 편이 좋습니다. 아래 그림처럼 90과 89, 60과 59를 넣으면 >=가 기준값을 포함하는지, 모든 조건이 거짓일 때 else에 도착하는지를 한 번에 확인할 수 있습니다. 또 else는 입력이 올바른지를 검사하지 않습니다. 이 체인에 150을 넣으면 A, -5를 넣으면 F가 나오므로, 0에서 100 사이인지는 체인 앞에서 별도의 조건으로 확인해야 합니다.
BRANCH REVIEW
조건 순서는 경계값과 함께 확인한다
대표값 하나만 보지 말고 경계의 바로 위·아래를 넣어 비교 연산자와 조건 순서를 함께 검증합니다.
| 입력 | 처음 true가 되는 조건 | 실행 결과 |
|---|---|---|
90 |
grade >= 90 |
A 블록 |
89 |
grade >= 80 |
B 블록 |
60 |
grade >= 60 |
D 블록 |
59 |
모든 조건이 false | else의 F 블록 |
90- 첫 true는
grade>= 90, 실행 결과는 A 블록입니다. 89- 첫 true는
grade >= 80, 실행 결과는 B 블록입니다. 60- 첫 true는
grade >= 60, 실행 결과는 D 블록입니다. 59- 모든 조건이 false이므로
else의 F 블록을 실행합니다.
포함 관계가 있으면 구체적인 조건부터
grade >= 60을 먼저 두면 90점도 그 블록에서 멈춰 아래의 grade >= 90에는 도달하지 못합니다.
else는 선택 사항
모든 조건이 false일 때 수행할 기본 동작이 있을 때 둡니다. 입력 유효성 검사는 별도 조건으로 표현해야 합니다.
한 문장이어도 중괄호 유지
나중에 문장을 추가해도 블록의 범위가 달라지지 않도록 {}로 의도를 고정합니다.
중첩이 깊으면 의도를 분리
관련 조건을 &&·||로 명확히 묶거나 함수·조기 반환으로 나누어 각 분기의 역할을 드러냅니다.
>=와 >의 차이, 넓은 조건이 세부 조건을 가리는 문제, 기본 경로의 누락을 빠르게 드러냅니다.독립된 if 여러 개와 하나의 else if 체인은 같은 조건을 써도 다르게 동작합니다. 다음 프로그램은 점수 95로 두 구조의 출력을 비교합니다.
#include <iostream>
int main() {
int grade{95};
if (grade >= 90) { std::cout << "A"; }
if (grade >= 80) { std::cout << "B"; }
if (grade >= 60) { std::cout << "D"; }
std::cout << '\n';
if (grade >= 90) { std::cout << "A"; }
else if (grade >= 80) { std::cout << "B"; }
else if (grade >= 60) { std::cout << "D"; }
else { std::cout << "F"; }
std::cout << '\n';
}ABD
A세 if는 각자 따로 검사하므로 95가 만족하는 세 조건의 블록을 모두 실행해 ABD를 출력합니다. 체인은 처음 참인 grade >= 90의 블록 하나만 실행하므로 A만 출력합니다. 조건마다 참인지 확인하는 것과, 그 구조가 블록을 몇 개 고르는지는 따로 확인해야 합니다.
정리
if의 조건은 문맥상bool로 변환되며, 정수는 0이 아니면, 포인터는 널이 아니면 참입니다.if-else는 두 블록 중 하나를,else if체인은 위에서부터 처음 참인 조건의 블록 하나만 실행합니다.else는 들여쓰기와 상관없이 가장 가까운 짝 없는if와 짝을 이루므로, 중첩if에는 중괄호로 범위를 밝힙니다.- 중괄호를 생략하면 덧붙인 문장이
if밖으로 빠지기 쉽고, 조건의=는 비교가 아니라 대입입니다. GCC의-Wall경고가 이런 실수를 알려 줍니다. - C++17의
if (초기화문; 조건)은 판단에 필요한 변수의 범위를if문 안으로 좁힙니다. - 체인은 좁은 조건부터 놓고, 기준값과 그 바로 아래 값으로 점검하며, 입력의 유효성은 별도 조건으로 검사합니다.
연습 문제
1. 기준값으로 학점 판단하기
grade_chain.cpp에서 grade만 59, 60, 90으로 각각 바꾸면 어떤 학점이 출력되나요? 그다음 grade >= 90과 grade >= 60 두 비교만 >로 바꾼 뒤 90과 60을 다시 넣으면 어떻게 되나요?
해설 보기
원래 조건에서는 59가 F, 60이 D, 90이 A입니다. 두 비교만 >로 바꾸면 90은 grade > 90이 거짓이지만 grade >= 80이 참이어서 B가 되고, 60은 grade > 60까지 모두 거짓이어서 F가 됩니다. 비교 연산자 하나가 기준값 자체를 어느 쪽에 넣는지 정하므로, 기준값과 그 바로 위아래 값을 따로 확인해야 합니다.
2. else의 짝
dangling_else.cpp의 코드에서 a와 b가 각각 (1, 1), (1, -1), (-1, 1)일 때 출력은 무엇인가요? 들여쓰기의 의도대로 동작하게 하려면 어떻게 고치나요?
해설 보기
else는 안쪽 if (b > 0)의 짝입니다. (1, 1)은 both positive, (1, -1)은 a is not positive를 출력하고, (-1, 1)은 바깥 조건이 거짓이어서 아무것도 출력하지 않습니다. 의도대로 하려면 if (a > 0) { if (b > 0) { ... } } else { ... }처럼 바깥 if의 블록을 중괄호로 닫은 뒤 else를 붙입니다.
3. 조건 자리의 대입
int x{3}; 다음에 if (x = 0) { std::cout << "zero\n"; } std::cout << x << '\n';를 실행하면 무엇이 출력되나요?
해설 보기
x = 0은 비교가 아니라 대입이므로 x가 0이 되고, 대입식의 값 0이 false로 해석되어 zero는 출력되지 않습니다. 이어서 바뀐 x의 값 0만 출력됩니다. 의도가 비교였다면 x == 0으로 고쳐야 하며, 이때는 x가 3이어서 역시 zero는 출력되지 않고 3이 출력됩니다.
다음 글 조건문 switch에서는 하나의 정수 값을 여러 상수와 비교해 실행할 위치를 고르는 switch 문을 다룹니다.