관계 연산자와 논리 연산자
프로그램은 단순히 계산만 하는 것이 아니라 특정 조건에 따라 다른 동작을 수행해야 할 때가 많습니다.
예를 들어 "만약 점수가 60점 이상이면 합격"과 같은 조건을 판단해야합니다.
이때 필요한 것이 바로 관계 연산자(Relational Operators) 와 논리 연산자(Logical Operators) 입니다.
이 장에서는 두 값의 크기를 비교하거나, 여러 조건을 조합하여 참(true) 또는 거짓(false)을 판단하는 방법에 대해 자세히 알아보겠습니다.
이 연산자들은 앞으로 배우게 될 조건문(if
), 반복문(for
, while
)의 핵심적인 구성 요소가 됩니다.
관계 연산자 (Relational Operators)
관계 연산자는 두 피연산자 간의 크기 또는 동등성 관계를 비교하여 그 결과로 true
(참) 또는 false
(거짓)의 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)
와 같이 실수로 대입 연산자를 사용하면 컴파일러가 경고를 주거나 (C++17부터는 특정 상황에서 오류로 처리될 수 있음), 의도치 않게a
에 10이 할당되고 조건문이 항상 참이 되는 심각한 버그를 유발할 수 있습니다. - 실수 비교의 주의: 부동 소수점 수(float, double)는 컴퓨터 내부 표현 방식 때문에 미세한 오차가 발생할 수 있습니다. 따라서 두 실수가 정확히 같은지
==
로 비교하는 것은 위험합니다. 대신, 두 실수의 차이가 아주 작은 값(epsilon
)보다 작으면 같다고 간주하는 방식을 사용합니다.double a = 0.1 + 0.2; // a는 정확히 0.3이 아닐 수 있음 (예: 0.30000000000000004) double b = 0.3; if (a == b) { // 대부분 false로 나옴 std::cout << "a와 b는 같다 (직접 비교)" << std::endl; } else { std::cout << "a와 b는 다르다 (직접 비교)" << std::endl; // 이 메시지가 출력될 확률이 높음 } const double EPSILON = 0.00000001; // 아주 작은 값 정의 if (std::abs(a - b) < EPSILON) { // std::abs는 <cmath>에 있음 std::cout << "a와 b는 거의 같다 (엡실론 비교)" << std::endl; // 이 메시지가 출력 }
#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
타입 피연산자를 사용하여 논리적인 연산(AND, OR, NOT)을 수행하고 그 결과로 true
또는 false
의 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) | 피연산자의 논리 값을 반전시킵니다. (true 는 false 로, false 는 true 로). | !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
: 만약A
가false
라면,B
의 값에 관계없이 전체 표현식은false
가 됩니다. 따라서B
는 평가되지 않습니다.A || B
: 만약A
가true
라면,B
의 값에 관계없이 전체 표현식은true
가 됩니다. 따라서B
는 평가되지 않습니다.
이러한 단락 평가는 특정 조건이 만족될 때만 안전하게 다음 코드를 실행해야 하는 경우에 유용합니다.
예를 들어 포인터가 유효한지 먼저 확인한 후, 그 포인터를 사용하는 코드를 실행할 때 활용됩니다.
// if (ptr != nullptr && ptr->member == value) { ... }
// 만약 ptr이 nullptr이면, ptr != nullptr이 false가 되어 ptr->member에 접근하지 않아 안전함.
#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;
}
연산자 우선순위와 결합 규칙
복잡한 표현식에서는 산술 연산자, 관계 연산자, 논리 연산자가 함께 사용될 수 있습니다.
이때 어떤 연산이 먼저 수행될지 이해하는 것이 중요합니다.
일반적인 우선순위는 다음과 같습니다 (높은 것에서 낮은 것 순)
- 단항 연산자 (
!
,++
,--
,+
,-
) - 산술 연산자 (
*
,/
,%
다음+
,-
) - 관계 연산자 (
<
,>
,<=
,>=
) - 관계 연산자 (
==
,!=
) - 논리 AND (
&&
) - 논리 OR (
||
) - 대입 연산자 (
=
,+=
,-=
등)
int x = 10, y = 5, z = 15;
bool result = x + y > z && z - y < x;
// 1. 산술 연산 먼저:
// x + y -> 10 + 5 = 15
// z - y -> 15 - 5 = 10
// 2. 관계 연산 다음:
// 15 > z -> 15 > 15 -> false
// 10 < x -> 10 < 10 -> false
// 3. 논리 AND 연산 마지막:
// false && false -> false
std::cout << "복합 조건 결과: " << std::boolalpha << result << std::endl; // 출력: false
이처럼 괄호를 적절히 사용하여 연산 순서를 명확히 하는 것이 코드의 가독성을 높이는 데 매우 중요합니다.