본문으로 건너뛰기

안동민 개발노트

본문 시작

관계 연산자와 논리 연산자

비교식이 만드는 bool 값과 논리 연산의 단락 평가를 이해하고 여러 조건을 정확한 판단식으로 조합합니다.

프로그램은 단순히 계산만 하는 것이 아니라 특정 조건에 따라 다른 동작을 수행해야 할 때가 많습니다.

예를 들어 만약 점수가 60점 이상이면 합격과 같은 조건을 판단해야 합니다.

이때 필요한 것이 바로 관계 연산자(Relational Operators)논리 연산자(Logical Operators)입니다.

이 장에서는 두 값의 크기를 비교하고, 여러 조건을 조합해 참(true)/거짓(false)을 판단하는 방법을 살펴봅니다.

이 연산자들은 앞으로 배우게 될 조건문(if), 반복문(for, while)의 핵심적인 구성 요소가 됩니다.


관계 연산자 (Relational Operators)

관계 연산자와 논리 연산자는 값의 형태, 식의 그룹핑, 평가 순서, 경계 입력을 구분해서 확인합니다.

기본 제공 관계 연산자와 동등 비교 연산자는 두 피연산자의 관계를 비교하고 bool 결과를 만듭니다. std::string 같은 타입이 제공하는 오버로드 연산자는 해당 타입의 별도 계약을 따르며, 모든 사용자 정의 비교 연산자가 반드시 bool을 반환하는 것은 아닙니다.

연산자이름설명예시 (결과)
==같다 (Equal to)두 피연산자의 값이 같으면 true, 아니면 false.5 == 5 (true)
!=같지 않다 (Not equal to)두 피연산자의 값이 다르면 true, 아니면 false.5 != 3 (true)
>보다 크다 (Greater than)왼쪽 피연산자가 오른쪽보다 크면 true.5 > 3 (true)
<보다 작다 (Less than)왼쪽 피연산자가 오른쪽보다 작으면 true.5 < 3 (false)
>=보다 크거나 같다 (Greater than or equal to)왼쪽 피연산자가 오른쪽보다 크거나 같으면 true.5 >= 5 (true)
<=보다 작거나 같다 (Less than or equal to)왼쪽 피연산자가 오른쪽보다 작거나 같으면 true.5 <= 3 (false)
주의사항
  • 같다(==)와 대입(=)의 혼동 금지: if (a = 10)은 언어 차원의 오류가 아니라 유효한 대입 표현식입니다. a10을 저장한 뒤 그 결과를 bool로 문맥 변환하므로 조건은 참이 됩니다. 많은 컴파일러가 경고하지만 경고에만 의존하지 말고 비교 의도를 a == 10으로 명시해야 합니다.

  • 실수 비교는 도메인 계약부터 정하기: 정확한 동일성이 필요한 값에는 ==가 맞을 수 있습니다. 반올림 오차를 허용해야 한다면 값의 스케일, 절대·상대 허용 오차, NaN과 무한대 정책을 먼저 정합니다. 고정 절대 허용 오차는 크기 범위를 아는 문제에만 적합한 한 가지 정책입니다.

    double a = 0.1 + 0.2;
    double b = 0.3;
    
    // 알려진 스케일에서 선택한 애플리케이션 정책의 예
    const double tolerance = 1e-8;
    if (std::isfinite(a) && std::isfinite(b) &&
        std::abs(a - b) <= tolerance) {
        std::cout << "정한 허용 오차 안에 있다\n";
    }
예시
#include <iostream> // std::cout, std::endl 사용
#include <string>   // std::string 사용
#include <cmath>    // std::abs 사용 (실수 비교를 위해)

int main() {
    int score = 75;
    int passingScore = 60;
    std::string name1 = "Alice";
    std::string name2 = "Bob";
    std::string name3 = "Alice";

    // 점수 비교
    bool isPassed = (score >= passingScore);
    std::cout << "합격 여부 (score >= passingScore): " << std::boolalpha << isPassed << std::endl; // true

    bool isPerfect = (score == 100);
    std::cout << "만점 여부 (score == 100): " << std::boolalpha << isPerfect << std::endl;   // false

    // 문자열 비교 (문자열 비교는 기본적으로 사전 순 비교)
    bool namesEqual = (name1 == name2);
    std::cout << "이름 같음 (name1 == name2): " << std::boolalpha << namesEqual << std::endl; // false

    bool namesNotEqual = (name1 != name3);
    std::cout << "이름 같지 않음 (name1 != name3): " << std::boolalpha << namesNotEqual << std::endl; // false

    bool nameOrder = (name1 < name2); // 'A'가 'B'보다 앞서므로 true
    std::cout << "사전순 비교 (name1 < name2): " << std::boolalpha << nameOrder << std::endl; // true

    return 0;
}

논리 연산자 (Logical Operators)

기본 제공 &&, ||, !의 피연산자는 bool로 문맥 변환되며, 연산 결과의 타입은 bool입니다.

관계 연산자의 결과(true/false)나 다른 bool 변수를 조합하여 더 복잡한 조건을 만들 때 사용됩니다.

연산자이름설명예시 (결과)
&&논리 AND (Logical AND)두 피연산자 모두 true일 때만 true, 아니면 false.(true && false) (false)
||논리 OR (Logical OR)두 피연산자 중 하나라도 true이면 true, 모두 false일 때만 false.(true || false) (true)
!논리 NOT (Logical NOT)피연산자의 논리 값을 반전시킵니다. (truefalse로, falsetrue로).!true (false)

논리 AND(&&): 두 조건이 모두 참일 때만 전체 결과가 참이 됩니다.

A && B

  • A가 true이고 B가 true일 때 -> true
  • A가 true이고 B가 false일 때 -> false
  • A가 false이고 B가 true일 때 -> false
  • A가 false이고 B가 false일 때 -> false

논리 OR(||): 두 조건 중 하나라도 참일 때 전체 결과가 참이 됩니다. A || B

  • A가 true이고 B가 true일 때 -> true
  • A가 true이고 B가 false일 때 -> true
  • A가 false이고 B가 true일 때 -> true
  • A가 false이고 B가 false일 때 -> false

논리 NOT(!): 단항 연산자로, 피연산자의 논리 값을 반전시킵니다.

!A

  • A가 true일 때 -> false
  • A가 false일 때 -> true

단락 평가 (Short-Circuit Evaluation): 기본 제공 &&|| 연산자는 단락 평가를 수행합니다.

이는 연산의 결과를 결정하기 위해 모든 피연산자를 평가할 필요가 없을 때, 더 이상 평가를 진행하지 않는다는 의미입니다.

  • A && B: 만약 Afalse라면, B의 값에 관계없이 전체 표현식은 false가 됩니다. 따라서 B는 평가되지 않습니다.
  • A || B: 만약 Atrue라면, B의 값에 관계없이 전체 표현식은 true가 됩니다. 따라서 B는 평가되지 않습니다.

이러한 단락 평가는 특정 조건이 만족될 때만 안전하게 다음 코드를 실행해야 하는 경우에 유용합니다.

예를 들어 포인터가 nullptr인지 먼저 확인한 뒤, null이 아닐 때만 역참조하는 코드를 실행할 수 있습니다.

아래 다이어그램은 왼쪽 값에 따라 오른쪽 피연산자를 평가하거나 건너뛰는 두 갈래를 직접 비교합니다.

기본 제공 논리 AND와 OR가 왼쪽 피연산자의 bool 값에 따라 오른쪽 피연산자를 평가하거나 건너뛰는 두 갈래 비교

BUILT-IN SHORT-CIRCUIT CONTRACT

왼쪽 A를 먼저 평가해 bool로 문맥 변환합니다. 그 값만으로 결과가 정해지지 않을 때에만 오른쪽 B를 평가합니다.

A && B

전체가 참이려면 두 피연산자가 모두 참이어야 합니다.

bool(A) == false
B 미평가 · 결과는 즉시 false
bool(A) == true
B 평가 · 결과는 bool(B)

A || B

전체가 거짓이려면 두 피연산자가 모두 거짓이어야 합니다.

bool(A) == true
B 미평가 · 결과는 즉시 true
bool(A) == false
B 평가 · 결과는 bool(B)

이 계약은 기본 제공 &&||에 적용됩니다. 논리 연산자를 오버로드한 함수 호출은 오른쪽 피연산자를 생략하지 않습니다.

단락 평가 예시
// if (ptr != nullptr && ptr->member == value) { ... }
// 만약 ptr이 nullptr이면, ptr != nullptr이 false가 되어 ptr->member에 접근하지 않아 안전함.

ptr != nullptr은 null인 경우만 제외합니다. null이 아닌 포인터도 이미 수명이 끝난 객체를 가리키는 dangling pointer일 수 있으므로, 이 검사만으로 포인터의 전체 유효성이 증명되지는 않습니다. 또한 사용자 정의 operator&&operator||는 함수 호출이므로 오른쪽 피연산자 평가를 생략하지 않습니다.

and, or, not, not_eq는 각각 &&, ||, !, !=와 동작이 같은 대체 표기입니다.

논리 연산자 예시
#include <iostream>

int main() {
    int age = 20;
    bool hasLicense = true;
    bool hasCar = false;
    int points = 85;

    // 성인이고 면허가 있는지 (AND)
    bool canDrive = (age >= 18) && hasLicense;
    std::cout << "운전 가능 (성인 && 면허): " << std::boolalpha << canDrive << std::endl; // true

    // 면허가 있거나 차가 있는지 (OR)
    bool needsTransport = hasLicense || hasCar;
    std::cout << "교통수단 필요 (면허 || 차): " << std::boolalpha << needsTransport << std::endl; // true (면허가 있으므로)

    // 시험에 불합격했는지 (NOT)
    bool isFailed = !(points >= 70); // 70점 이상이면 합격이므로, 그 반대는 불합격
    std::cout << "시험 불합격 여부: " << std::boolalpha << isFailed << std::endl; // false (85점은 합격)

    // 복합 조건
    bool canRentCar = (age >= 21) && hasLicense && (points >= 80);
    std::cout << "렌터카 가능 (복합 조건): " << std::boolalpha << canRentCar << std::endl; // false (age >= 21이 false)

    return 0;
}

C++ 연산자 우선순위와 결합 규칙

복잡한 표현식에서는 산술 연산자, 관계 연산자, 논리 연산자가 함께 사용될 수 있습니다.

우선순위와 결합 규칙은 식이 어떻게 묶이는지를 정합니다. 일반적인 피연산자 평가 순서를 정하는 규칙과는 구분해야 합니다.

이 문서에 등장하는 연산자의 우선순위는 다음과 같습니다(높은 것에서 낮은 것 순).

후위 연산자 (expr++, expr--)

전위·단항 연산자 (!, ++expr, --expr, +, -)

산술 연산자 (*, /, % 다음 +, -)

관계 연산자 (<, >, <=, >=)

동등 비교 연산자 (==, !=)

논리 AND (&&)

논리 OR (||)

대입 연산자 (=, +=, -= 등)

예시
int x = 10, y = 5, z = 15;
bool result = x + y > z && z - y < x;
// 그룹핑: ((x + y) > z) && ((z - y) < x)
// 왼쪽: 10 + 5 > 15 -> 15 > 15 -> false
// 기본 제공 &&는 여기서 단락 평가하므로 z - y < x는 평가하지 않음
// 결과: false
std::cout << "복합 조건 결과: " << std::boolalpha << result << std::endl; // 출력: false

괄호는 의도한 그룹핑을 명확하게 하지만, 한 그룹 안에서 일반적인 피연산자 평가 순서를 지정하는 도구는 아닙니다.

수학식처럼 비교를 이어 쓰면 C++에서는 다른 식으로 묶입니다.

범위 비교
0 <= x <= 10;       // (0 <= x) <= 10: 원하는 범위 검사가 아님
0 <= x && x <= 10;  // 올바른 범위 검사