연산자 우선순위 (Operator precedence)
4 + 2 * 3 과 같은 복합 표현식의 파싱을 돕기 위해 모든 연산자에는 우선순위 레벨이 할당되어 있습니다.
- 더 높은 우선순위를 가진
연산자가 피연산자와 먼저 그룹화됩니다.
- 일반적으로 곱셈과 나눗셈(우선순위 레벨 5)은 덧셈과 뺄셈(우선순위 레벨 6)보다 높은 우선순위를 가집니다.
- 따라서 곱셈과 나눗셈은 덧셈과 뺄셈보다 먼저 피연산자와 그룹화됩니다.
- 즉,
4 + 2 * 3은 4 + (2 * 3)으로 그룹화됩니다.
연산자 결합 법칙 (Operator associativity)
- 동일한 우선순위를 가진 두 연산자가 표현식에서 서로 인접해 있는 경우,
- 연산자의 결합 법칙은 컴파일러에게 연산자를 왼쪽에서 오른쪽으로 평가할지,
- 오른쪽에서 왼쪽으로 평가할지를 알려줍니다.
연산자 우선순위(Precedence)와 결합성(Associativity) 표
- 아래 표는 연산자
우선순위/결합성을 빠르게 확인하기 위한 참고표입니다.
- 연산자 우선순위나 결합성에 대해 헷갈릴 때 다시 찾아볼 수 있도록 만들어졌습니다.
참고(Notes)
- 우선순위 레벨 1이 가장 높고, 레벨 17이 가장 낮습니다.
- 숫자가 작을수록 더 먼저 묶여서 계산됩니다.
L→R 좌측 결합
R→L 우측 결합

괄호를 사용하여 복합 표현식을 이해하기 쉽게 만들기
- 연산자와 우선순위 레벨이 너무 많아서 모두 기억하기 어렵습니다.
- 또한 복합 표현식이 어떻게 평가되는지 이해하기 위해 매번 연산자 표를 찾아보고 싶지는 않을 것입니다.
- 실수를 줄이고 우선순위 표를 참조하지 않고도 코드를 이해하기 쉽게 만들기 위해, 계산 순서가 한눈에 들어오지 않는 복합 표현식은 괄호로 묶어 의도를 명확히 하는 것이 좋습니다.
연산의 값 계산 (Value computation of operations)
- C++에서는 더하기나 곱하기 같은 기호(연산자)를 써서 계산 결과를 만들어내는 과정을 값 계산이라고 불러요.
- 이런 계산을 할 때, 어떤 기호를 먼저 계산할지, 그리고 순서가 같다면 어느 방향(왼쪽 또는 오른쪽)부터 풀어나갈지 결정하는 규칙이 바로 우선순위와 결합 법칙입니다.
피연산자의 평가 (Evaluation of operands)
- C++ 공식 규칙에서는 '평가'를 요리에 쓰일 '재료를 준비하는 과정'으로만 봅니다.
- 예를 들어
a + b를 할 때, a가 얼마인지 확인하고 b가 얼마인지 확인하는 과정까지만 평가라고 부르죠.
- 그리고 이 재료들을 더해서(+) 최종 결과를 내는 건
값 계산이라고 따로 부릅니다.
- 하지만 우리 개발자들이 평소에 대화할 때는 이걸 굳이 나누지 않고,
a + b의 정답을 구해내는 전체 과정을 뭉뚱그려서 평가한다고 말합니다.
단항 산술 연산자 (Unary arithmetic operators)
- 단항 산술 연산자에는
+ 와 - 두 가지가 있습니다.
- 상기하자면, 단항 연산자는 하나의 피연산자만 취하는 연산자를 말합니다.
이항 산술 연산자 (Binary arithmetic operators)
• 5가지의 이항 산술 연산자가 있습니다.
• 이항 연산자는 왼쪽과 오른쪽, 두 개의 피연산자를 취하는 연산자입니다.
| 연산자 (Operator) | 기호 (Symbol) | 형태 (Form) | 연산 내용 (Operation) |
|---|
| 덧셈 (Addition) | + | x + y | x 더하기 y |
| 뺄셈 (Subtraction) | - | x - y | x 빼기 y |
| 곱셈 (Multiplication) | * | x * y | x 곱하기 y |
| 나눗셈 (Division) | / | x / y | x 나누기 y |
| 나머지 (Remainder) | % | x % y | x를 y로 나눈 나머지 |
부동 소수점 나눗셈 (floating point division)
- 만약 피연산자 중 하나가 부동 소수점 값이라면, 나눗셈 연산자는 부동 소수점 나눗셈을 수행합니다.
- 부동 소수점 나눗셈은 부동 소수점 값을 반환하며 소수점 이하 부분이 유지됩니다.
- 예를 들어,
7.0 / 4 = 1.75 7 / 4.0 = 1.75 7.0 / 4.0 = 1.75입니다.
- 모든 부동 소수점 산술 연산과 마찬가지로 반올림 오차가 발생할 수 있습니다.
static_cast<>를 사용하여 정수끼리 부동 소수점 나눗셈하기
- 만약 두 개의 정수가 있고, 소수점 이하를 잃지 않고 나누고 싶다면 어떻게 해야 할까요?
static_cast<>를 사용하여 정수를 부동 소수점 숫자로 변환하면, 정수 나눗셈 대신 부동 소수점 나눗셈을 수행할 수 있습니다.
std::cout << "double / int = " << static_cast<double>(x) / y << '\n';
0과 0.0으로 나누기 (Dividing by 0 and 0.0)
산술 대입 연산자 (Arithmetic assignment operators)
x = x + 4와 같은 구문을 작성하는 것은 매우 흔한 일이기 때문에, C++은 편의를 위해 5가지의 산술 대입 연산자를 제공합니다.
x = x + 4 대신 x += 4라고 쓸 수 있습니다.
x = x * y 대신 x *= y라고 쓸 수 있습니다.
| 연산자 (Operator) | 기호 (Symbol) | 형태 (Form) | 연산 내용 (Operation) |
|---|
| 덧셈 대입 (Addition assignment) | += | x += y | x에 y를 더함 (x = x + y) |
| 뺄셈 대입 (Subtraction assignment) | -= | x -= y | x에서 y를 뺌 (x = x - y) |
| 곱셈 대입 (Multiplication assignment) | *= | x *= y | x에 y를 곱함 (x = x * y) |
| 나눗셈 대입 (Division assignment) | /= | x /= y | x를 y로 나눔 (x = x / y) |
| 나머지 대입 (Remainder assignment) | %= | x %= y | x에 (x를 y로 나눈) 나머지를 대입 (x = x % y) |
값을 변경하는 연산자와 변경하지 않는 연산자
1. 대부분의 기호는 원본을 건드리지 않습니다 (비수정 연산자)
우리가 아는 + - * / 같은 대부분의 C++ 연산자들은 '읽기 전용'입니다.
예를 들어 a + b를 계산한다고 해서 변수 a나 b에 들어있던 원래 숫자가 변하지는 않습니다.
그저 값을 복사해 와서 새로운 결과를 만들어낼 뿐입니다.
2. 원본 값을 직접 바꿔버리는 녀석들 (수정 연산자)
반면, 계산을 수행하면서 변수 안의 내용물을 아예 새로운 값으로 덮어써 버리는 기호들이 있습니다.
C++에서는 이를 비공식적으로 '수정 연산자'라고 부르며, 크게 두 가지 종류가 있습니다.
- 값을 욱여넣는 기호들 (대입 연산자)
- 기본 대입 (
=) 오른쪽 값을 왼쪽 변수에 통째로 덮어씁니다. (예: a = 5)
- 계산 후 대입 (
+= -= *= 등) 계산한 결과를 다시 자기 자신에게 덮어씁니다. (예: a += 2는 a에 2를 더한 뒤, 그 결과를 다시 원본 a에 저장합니다.)
- 비트 대입 (
<<= &= 등) 0과 1의 비트 단위 계산을 한 뒤 자기 자신에게 덮어씁니다.
- 1씩 올리거나 내리는 기호들 (증감 연산자)
++ -- 변수의 원본 값을 1만큼 직접 증가시키거나 감소시킵니다. (예: a++를 실행하면 a의 값이 진짜로 1 커집니다.)
나머지 연산자 (The remainder operator, operator%)
- 나머지 연산자는 정수 나눗셈을 수행한 후의 나머지를 반환하는 연산자입니다.
- 나머지 연산자는 정수 피연산자 에서만 동작합니다.
- 흔히 모듈로(modulo) 또는 모듈러스(modulus) 연산자라고도 부릅니다.
- 이 연산자는 어떤 숫자가 다른 숫자로 나누어 떨어지는지 확인할 때 가장 유용합니다.
- 만약
x % y 의 결과가 0이라면, x는 y로 나누어 떨어진다는 것을 알 수 있습니다.
- 피연산자가 음수가 될 수 있는 경우, 나머지값도 음수가 될 수 있다는 점에 주의해야 합니다.
- 가능하다면 나눠떨어지는지 확인하기 위한 표현식을 작성할때 나머지 연산자의 결과는
0과 비교하는 것을 선호하십시오.
- 두 번째 숫자가 첫 번째 숫자보다 클 때, 두 번째 숫자는 첫 번째 숫자를
0번 나누게 되므로, 첫 번째 숫자가 그대로 나머지가 됩니다.
거듭제곱 연산자는 어디 있나요? (Where’s the exponent operator?)
- C++는 별도의 거듭제곱 연산자가 없습니다.
- C++에서 거듭제곱을 하려면
<cmath> 헤더를 포함하고 pow() 함수를 사용해야 합니다
• #include <cmath>
• double x{ std::pow(3.0, 4.0) };
pow() 함수의 매개변수(그리고 반환값)는 double 타입이라는 점에 유의하세요.
- 부동 소수점 숫자의 반올림 오차로 인해, 정수를 전달하더라도
pow()의 결과가 정확하지 않을 수 있습니다.
변수 증감 (Incrementing and decrementing variables)
- 변수의 값을 증가
1을 더함 시키거나 감소 1을 뺌 시키는 작업은 매우 빈번하게 발생하기 때문에, 이를 위한 전용 연산자가 존재합니다.
| 연산자 | 기호 | 형태 | 작동 방식 |
|---|
| 전위 증가 (Prefix increment) | ++ | ++x | x를 증가시킨 후, x를 반환합니다. |
| 전위 감소 (Prefix decrement) | -- | --x | x를 감소시킨 후, x를 반환합니다. |
| 후위 증가 (Postfix increment) | ++ | x++ | x를 복사하고, x를 증가시킨 뒤, 복사본을 반환합니다. |
| 후위 감소 (Postfix decrement) | -- | x-- | x를 복사하고, x를 감소시킨 뒤, 복사본을 반환합니다. |
- 각 연산자에는 두 가지 버전이 있다는 점에 유의하세요.
- 연산자가 피연산자 앞에 오는 전위(prefix) 버전
- 연산자가 피연산자 뒤에 오는 후위(postfix) 버전
전위 증가 및 감소 (Prefix increment and decrement)
- 전위 증감 연산자는 매우 직관적입니다.
- 먼저 피연산자가 증가하거나 감소하고, 그 표현식은 피연산자의 값으로 평가됩니다.
후위 증가 및 감소 (Postfix increment and decrement)
- 후위 증감 연산자는 조금 더 까다롭습니다.
- 먼저 피연산자의 복사본을 만듭니다.
- 그 다음 복사본이 아닌 피연산자를 증가하거나 감소시킵니다.
- 마지막으로 복사본이 평가됩니다.
쉼표 연산자 (The comma operator)
- 원래 코드 하나만 써야 하는 단일 표현식이라도
, 로 묶어주면 여러 개의 코드를 다중 표현식 처럼 평가할 수 있게 해줍니다.
- 쉼표 연산자는 왼쪽 피연산자를 평가한 다음, 오른쪽 피연산자를 평가하고, 오른쪽 피연산자의 결과를 반환합니다.
- 대부분의 프로그래머는 쉼표 연산자를 거의 사용하지 않지만, 유일한 예외로
for 루프(loops) 내부에서는 꽤 흔하게 사용됩니다.
for 루프 내부를 제외하고는 쉼표 연산자 사용을 피하십시오.
조건 연산자 (The conditional operator)
- 조건 연산자
?: 는 삼항 연산자입니다. 즉 3개의 피연산자를 취하는 연산자입니다.
?: 연산자는 특정 유형의 if-else 문을 짧게 줄여 쓰는 방법을 제공합니다.
| 연산자 | 기호 | 형식 | 의미 |
|---|
| 조건(삼항) 연산자 (Conditional/Ternary) | ?: | c ? x : y | 조건 c가 참(true)이면 x를 평가하고, 그렇지 않으면 y를 평가합니다. |
?: 연산자는 다음과 같은 형태를 취합니다.
condition ? expression1 : expression2;
condition이 참으로 평가되면 expression1이 평가되고, 그렇지 않으면 expression2가 평가됩니다.
조건 연산자는 표현식의 일부로 평가됩니다
- 조건 연산자는 표현식의 일부로 평가되므로, 표현식이 허용되는 곳이라면 어디든 사용할 수 있습니다.
- 조건 연산자의 피연산자가 상수 표현식인 경우, 조건 연산자 자체도 상수 표현식으로 사용할 수 있습니다.
- 복합 표현식(다른 연산자가 포함된 표현식)에서 사용될 때는 조건 연산 전체(피연산자 포함)를 괄호로 감싸십시오.
- 복잡한 표현식에서는 조건 연산자 사용을 피하십시오.
언제 조건 연산자를 사용해야 할까요?
- 두 값 중 하나로 객체를 초기화할 때
- 객체에 두 값 중 하나를 대입할 때
- 함수에 두 값 중 하나를 전달할 때
- 함수에서 두 값 중 하나를 반환할 때
- 두 값 중 하나를 출력할 때
관계 연산자(Relational operators)
- 관계 연산자는 두 값을 비교할 때 사용하는 연산자입니다.
- C++에는 6가지 관계 연산자가 있습니다.
| 연산자 | 기호 | 형식 | 동작 |
|---|
| Greater than (보다 큼/초과) | > | x > y | x가 y보다 크면 true, 아니면 false |
| Less than (보다 작음/미만) | < | x < y | x가 y보다 작으면 true, 아니면 false |
| Greater than or equals (크거나 같음/이상) | >= | x >= y | x가 y보다 크거나 같으면 true, 아니면 false |
| Less than or equals (작거나 같음/이하) | <= | x <= y | x가 y보다 작거나 같으면 true, 아니면 false |
| Equality (동등/같음) | == | x == y | x와 y가 같으면 true, 아니면 false |
| Inequality (부등/다르다) | != | x != y | x와 y가 같지 않으면 true, 아니면 false |
- 관계 연산자를 사용하여 부동 소수점 값을 비교하는 것은 위험할 수 있습니다.
- 이는 부동 소수점 값이 정밀하지 않으며, 피연산자의 작은 반올림 오차로 인해 예상보다 약간 작거나 클 수 있기 때문입니다.
부동 소수점과 관계 연산자
- 부동 소수점 값에 관계 연산자를 사용할 때, 대부분의 경우에는 신뢰할 수 있는 결과를 얻습니다.
- 하지만 피연산자가 거의 동일한 경우, 이 연산자들은 신뢰할 수 없는 것으로 간주해야 합니다.
- 특히
operator==와 operator!=를 사용하지 마십시오.
팁 (Tip)
- 컴퓨터의 오차는 일관성이 있습니다.
- 그래서
float은 float 숫자끼리, double은 double 숫자끼리 비교하는 것은 괜찮습니다.
- 하지만 두 타입을 섞어서 비교하면 오차의 모양이 달라져서 프로그램이 망가집니다.
논리 연산자 (Logical operators)
| Operator | Symbol | Example Usage | Operation |
|---|
| Logical NOT | ! | !x | x가 false이면 true, x가 true이면 false |
| Logical AND | && | x && y | x와 y가 둘 다 true이면 true, 아니면 false |
| Logical OR | || | x || y | x 또는 y가 true이면(둘 중 하나 또는 둘 다) true, 아니면 false |
논리 NOT (Logical NOT)
- 논리 NOT의 피연산자가 참으로 평가되면, 논리 NOT은 거짓으로 평가됩니다.
- 반대로 피연산자가 거짓으로 평가되면, 논리 NOT은 참으로 평가됩니다.
- 즉, 논리 NOT은 불리언 값을 참에서 거짓으로, 또는 그 반대로 뒤집습니다.
- 논리 NOT은 조건문에서 자주 사용됩니다.
- 주의해야 할 점은 논리 NOT 연산자의 우선순위가 매우 높다는 것입니다.
논리 OR (Logical OR)
- 논리 OR 연산자는 두 조건 중 하나라도 참인지 테스트하는 데 사용됩니다.
- 왼쪽 피연산자가 참이거나, 오른쪽 피연산자가 참이거나, 둘 다 참인 경우 논리 OR 연산자는 참을 반환합니다.
- 그렇지 않으면 거짓을 반환합니다.
- 논리 OR 문은 여러 개를 연결할 수 있습니다.
if (value == 0 || value == 1 || value == 2 || value == 3)
논리 AND (Logical AND)
- 논리 AND 연산자는 두 피연산자가 모두 참인지 테스트하는 데 사용됩니다.
- 두 피연산자가 모두 참이면 논리 AND는 참을 반환합니다.
- 그렇지 않으면 거짓을 반환합니다.
- 논리 OR와 마찬가지로 논리 AND 문도 여러 개를 연결할 수 있습니다.
if (value > 10 && value < 20 && value != 16)
단락 평가 (Short circuit evaluation)
- 논리 AND가 참을 반환하려면 두 피연산자가 모두 참으로 평가되어야 합니다.
- 만약 왼쪽 피연산자가 거짓으로 평가되면, 논리 AND는 오른쪽 피연산자가 참인지 거짓인지와 상관없이 거짓을 반환해야 함을 알게 됩니다.
- 이 경우, 논리 AND 연산자는 오른쪽 피연산자를 평가하지 않고 즉시 거짓을 반환합니다.
- 이것을 단락 평가라고 하며, 주로 최적화를 목적으로 수행됩니다.
드모르간의 법칙 (De Morgan’s laws)
- 많은 프로그래머가
!(x && y)가 !x && !y와 같다고 생각하는 실수를 범합니다.
- 불행히도 논리 NOT은 그런 식으로 분배될 수 없습니다.
- 드모르간의 법칙(De Morgan’s laws)은 이러한 경우 논리 NOT이 어떻게 분배되어야 하는지 알려줍니다.
!(x && y)는 !x || !y와 동등합니다.
!(x || y)는 !x && !y와 동등합니다.
- 즉, 논리 NOT을 분배할 때는 논리 AND를 논리 OR로, 또는 그 반대로 뒤집어야 합니다!
- 이는 때때로 복잡한 표현식을 읽기 쉽게 만들 때 유용할 수 있습니다.
논리적 배타적 논리합(XOR) 연산자는 어디에 있나요?
- C++는 명시적인 논리 XOR 연산자를 제공하지 않습니다.
| 왼쪽 피연산자 | 오른쪽 피연산자 | 결과 |
|---|
| false | false | false |
| false | true | true |
| true | false | true |
| true | true | false |
- 그러나
bool 피연산자가 주어졌을 때 operator!=는 논리 XOR과 동일한 결과를 생성합니다.
| 왼쪽 피연산자 | 오른쪽 피연산자 | 논리 XOR | operator != |
|---|
| false | false | false | false |
| false | true | true | true |
| true | false | true | true |
| true | true | false | false |
- 따라서 논리
XOR는 다음과 같이 구현할 수 있습니다.
if (a != b) ...
- 이는 다음과 같이 여러 피연산자로 확장될 수 있습니다.
if (a != b != c) ...
- 이 식은 피연산자(a, b, c) 중 홀수 개가 참으로 평가될 때 true로 평가됩니다.
- 피연산자가
bool 타입이 아닌 경우, operator!=를 사용하여 논리 XOR를 구현하면 예상대로 작동하지 않습니다.