C++ 1.8 Tests 정리: 조건식, short-circuit, if 초기화, switch fall-through, vector::size() | slug: 20260912-cpp-1-8-tests-merged | series: A Tour of C++ 1.8

A Tour of C++ §1.8 Tests는 선택과 반복을 어떻게 표현하는지를 다룹니다. C++는 if, switch, while, for로 분기와 루프를 만들고, 그 조건 자리에서는 정수·포인터·논리 연산·삼항 연산자까지 같은 방식으로 “실행할지 말지”를 결정합니다.

이번 글에서는 문법 나열보다, 공부하면서 실제로 헷갈리기 쉬운 지점—truthiness, short-circuit, 연속 비교의 함정, 삼항 연산자, if 초기화의 세미콜론, switch fall-through, size()capacity(), 그리고 FAQ·디버깅 체크리스트—를 한 흐름으로 정리합니다.

핵심 아이디어: 선택과 반복

C++에서 프로그램의 흐름을 바꾸는 대표 구문은 다음과 같습니다.

구문역할
if조건에 따른 선택(분기)
switch여러 상수 값 중 하나로 분기
while / for조건이 참인 동안 반복

입출력도 함께 자주 등장합니다. cout <<는 출력, cin >>는 입력입니다. >>오른쪽 변수 타입이 무엇을 읽을지(문자, 정수 등)를 정합니다. 또한 변수는 파일 맨 위에 모두 모아 둘 필요 없이, 필요한 순간에 선언해도 됩니다.

C++에서 무엇이 true인가?

if, while, 조건 연산자 ?:의 첫 번째 식처럼 조건이 필요한 자리에서는 값이 Boolean 판단으로 변환됩니다.

표현식조건 결과의미
0false정수 0은 false입니다.
0이 아닌 정수true1, -1, 42 모두 true입니다.
nullptrfalse포인터가 어떤 객체도 가리키지 않습니다.
null이 아닌 포인터true유효 여부를 검사할 수 있는 주소값이 들어 있습니다.
int count = 3;
if (count) {
    // 실행됨: 3은 0이 아님
}

int* p = nullptr;
if (p) {
    // 실행되지 않음
}

중요: true는 정수값이 꼭 1이라는 뜻이 아닙니다. 조건식 자리에서 true로 변환되는 값이라는 뜻입니다.

논리 연산자와 short-circuit evaluation

&&는 “둘 다 참이어야 함”, ||는 “하나만 참이어도 됨”을 뜻합니다. 여기서 핵심은 결과가 이미 결정되면 뒤의 식을 실행하지 않을 수 있다는 점입니다. 이를 **short-circuit evaluation(단락 평가)**이라고 합니다.

bool is_valid = ptr != nullptr && ptr->ready();

왼쪽의 ptr != nullptr가 false라면 C++은 오른쪽의 ptr->ready()를 호출하지 않습니다. 따라서 null pointer를 역참조하는 일을 막을 수 있습니다. 성능 최적화이기도 하지만, 더 본질적으로는 안전 규칙입니다.

bool open = true;
if (open || expensive_check()) {
    // expensive_check()는 호출되지 않음
}

||에서는 왼쪽이 true이면 전체 결과가 true로 확정됩니다. 반대로 &&에서는 왼쪽이 false이면 전체 결과가 false로 확정됩니다.

수학식처럼 연속 비교를 쓰면 안 되는 이유

아래 식은 수학적으로는 자연스러워 보이지만 C++에서 범위 검사가 아닙니다.

if (5 < x < 20) {  // 잘못된 범위 검사
}

C++은 왼쪽부터 계산합니다. 먼저 5 < xtrue 또는 false가 되고, 그 결과가 다시 20과 비교됩니다. 범위 검사는 반드시 다음처럼 각각 적어야 합니다.

if (5 < x && x < 20) {
    // x가 5보다 크고 20보다 작을 때만 실행
}

조건 연산자: condition ? a : b

조건 연산자는 두 값 중 하나를 선택할 때 사용합니다. 두 분기가 짧고 하나의 값으로 자연스럽게 표현될 때 잘 어울립니다.

const char* mode = hot ? "Hot" : "Normal";
int limit = enabled ? 100 : 0;

hot이 true이면 "Hot", false이면 "Normal"을 선택한다는 뜻입니다. 여러 줄의 처리나 부작용(side effect)이 있다면 일반적인 if/else가 더 읽기 좋습니다.

if 초기화란? if (초기화; 조건)

C++에서는 if 안에서 변수를 만들고 바로 검사할 수 있습니다.

if (auto n = v.size(); n!=0) {
    // n을 사용하는 코드
}

처음에는 세미콜론 앞뒤를 둘 다 조건으로 검사한다고 오해하기 쉽습니다. 그렇지 않습니다.

위치의미
; 변수 생성·초기화. 참/거짓 검사가 아닙니다. 항상 실행됩니다.
; if에 들어갈지를 정하는 유일한 조건입니다.

공부하면서 잡은 핵심: if (auto n = v.size(); n!=0)에서 앞부분은 “n을 만든다”이고, 뒷부분만 “들어갈지 말지”를 결정합니다.

짧은 형태 if (auto n = v.size())

세미콜론이 없으면, 만든 값 n 자체가 조건이 됩니다. 숫자 0은 거짓, 0이 아니면 참입니다.

if (auto n = v.size()) {
    // n != 0 인 경우와 동일 (size()가 정수일 때)
}

v.size()는 보통 0 또는 양수입니다. 따라서 이 예에서 if (n)if (n!=0)같은 뜻입니다. n!=0이 “원소가 있는지”를 추가로 확인하는 별도 검사가 아닙니다. 짧은 형태가 바로 그 검사입니다.

세미콜론 형태가 필요한 경우는 조건이 n!=0이 아닐 때입니다. 예를 들어 n > 10처럼 임계값이 다르면 초기화와 조건을 분리해야 합니다.

if (auto n = v.size(); n > 10) {
    // 원소 개수가 10보다 클 때만
}

변수 n의 범위(scope)

n은 그 if의 참 가지와 거짓 가지 안에서만 사용할 수 있습니다. if 문이 끝난 뒤에는 n이 없습니다.

if (auto n = v.size(); n!=0) {
    // 여기서는 n 사용 가능
} else {
    // else 안에서도 n 사용 가능
}
// 여기서 n을 쓰면 오류

switch와 break: fall-through는 언제 생기나?

switch의 각 case서로 다른 상수여야 합니다. 맞는 case가 없고 default도 없으면 아무 일도 하지 않습니다. default는 선택 사항입니다.

break가 없으면 fall-through가 발생합니다. 즉, 다음 case의 코드가 이어서 실행되고, break(또는 return)을 만날 때까지 계속됩니다. 버그처럼 보이지만, 의도적으로 case를 붙여 같은 동작을 공유하기도 합니다.

switch (ch) {
case 'u':
case 'n':
    // 'u'와 'n'이 같은 처리를 공유 (의도적 fall-through)
    break;
default:
    break;
}

학습 포인트: fall-through는 “아래로 넘어간다”는 뜻입니다. return을 만나면 함수를 떠나므로 그 지점에서 멈춥니다.

자주 헷갈리는 지점: vector::size()와 capacity

std::vector<int> v;

if (auto n = v.size()) {
    std::cout << "A";
} else {
    std::cout << "B";
}

출력은 B입니다. 빈 vector의 v.size()는 0이기 때문입니다. 이때 vector 객체가 없는 것이 아닙니다. size()는 현재 들어 있는 원소 수를 의미할 뿐, 내부에 메모리가 확보되어 있는지와는 다른 질문입니다.

멤버 함수알려주는 값
v.size()현재 vector에 들어 있는 원소의 개수
v.capacity()재할당 없이 담을 수 있도록 확보된 공간의 크기
std::vector<int> v {10};
if (auto n = v.size()) {
    std::cout << "A";  // n은 1, 따라서 A 출력
}

기억할 문장: 빈 vector는 “없는 vector”가 아니라 “원소가 0개인 vector”입니다.

자주 헷갈리는 질문 FAQ

accept()에서 y가 아니면 왜 false인가?

“bool 함수라서 return하니까 y일 때만 true”라고 생각하기 쉽습니다. 정확히는 bool 함수라서가 아닙니다. 코드에 if (answer == 'y') return true;만 있고, 그 외 모든 경우는 바로 return false;로 떨어지기 때문입니다. 그래서 'n''x'를 구분하지 않습니다. (책의 accept2처럼 switch로 n/default를 나누면 다르게 설계할 수 있습니다.)

switch에서 break를 빼면?

fall-through가 발생해 아래로 넘어가고, break(또는 return)가 있는 곳까지 진행됩니다. 의도적으로 case를 붙여 같은 동작을 공유할 수 있습니다.

if (auto n = v.size(); n!=0)if (auto n = v.size())는 다른가?

이 예에서는 같습니다. v.size()는 0 또는 양수이고, C++에서 숫자 0은 거짓·0이 아니면 참이므로 if (n)if (n!=0)은 정수 size()에 대해 같은 뜻입니다. 세미콜론 형태는 초기화와 조건이 다를 때(n>10 등) 필요합니다.

n을 if 바깥에서 쓸 수 있나?

없습니다. if의 참/거짓 가지 안에서는 쓸 수 있고, if가 끝난 뒤에는 없습니다.

실무 코드에서 조건식을 읽는 순서

펌웨어, 데몬, 애플리케이션 코드를 읽거나 로그를 디버깅할 때는 조건식을 아래 세 단계로 나누어 보면 좋습니다.

  1. 각 하위 표현식은 어떤 값을 만드는가?
  2. 그 값은 조건식에서 true/false로 어떻게 해석되는가?
  3. &&, || 때문에 뒤의 함수 호출이나 접근이 생략될 수 있는가?

예를 들어 if (device && device->is_ready())는 먼저 포인터 존재 여부를 확인하고, 포인터가 있을 때만 상태를 확인합니다. 이 순서 자체가 코드의 안전 로직입니다.

요약

  • 선택·반복의 기본은 if / switch / while / for입니다.
  • 0, nullptr은 false이고, 0이 아닌 정수와 null이 아닌 포인터는 조건식에서 true입니다.
  • &&는 왼쪽이 false이면 멈추고, ||는 왼쪽이 true이면 멈춥니다.
  • 범위 비교는 low < x && x < high처럼 작성합니다.
  • 작은 두 갈래 값 선택에는 condition ? a : b를 사용할 수 있습니다.
  • if (init; cond)에서 세미콜론 앞은 초기화(항상 실행), 뒤만 조건입니다.
  • if (auto n = v.size())는 size() 정수에 대해 n!=0과 같습니다.
  • switch는 상수 case, 선택적 default, break 없으면 fall-through입니다.
  • 빈 vector는 존재하며, 단지 size()가 0입니다. capacity()와는 다른 질문입니다.
  • accept()가 non-y에서 false인 이유는 “bool이라서”가 아니라 분기 설계 때문입니다.
  • if에서 만든 n은 if 가지 밖으로 나가지 않습니다.

다음: §1.9 Mapping to Hardware

다음에는 이 추상적인 제어 흐름이 하드웨어와 어떻게 대응되는지(§1.9 Mapping to Hardware)를 이어서 정리할 예정입니다.