C++ 1.8 Tests 정리: 조건식, short-circuit, if 초기화, switch fall-through, vector::size() | slug: 20260917-tour-cpp-1-8-tests | 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·디버깅 체크리스트—를 한 흐름으로 정리합니다.

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

선택·반복 흐름 (Arrange-Act-Assert 관점)

선택과 반복: 조건을 준비하고(Arrange), 실행하고(Act), 분기 결과를 확인(Assert)

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가 끝난 뒤에는 없습니다.

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

assertion이 잡는 버그 vs 조건식 오해

assertion이 잡는 버그와 조건식 오독을 구분하기

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

  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)를 이어서 정리할 예정입니다.