연산자가 하는 일
연산자는 피연산자라고 부르는 하나 이상의 값에 대해 동작을 수행하는 기호입니다. 이미 하나를 사용해 왔습니다. 값을 변수에 묶어 주는 =입니다. C++에는 수학, 비교, 논리, 비트 조작을 위한 풍부한 연산자 모음이 들어 있으며, 초보자가 걸려 넘어지는 날카로운 모서리도 몇 가지 있습니다.
이전 페이지에서는 const가 값을 고정하는 모습을 보았습니다. 연산자는 그 값이 상수든 아니든, 저장할 값을 계산하는 방법입니다. 매일 사용하게 될 연산자 그룹을 하나씩 살펴보겠습니다.
산술 연산자
다섯 가지 산술 연산자는 +, -, *, /, 그리고 %(나머지 연산, 나눗셈의 나머지)입니다.
가장 큰 함정은 바로 그 몫에 있습니다. 17 / 5는 3.4가 아니라 3을 출력합니다. 두 피연산자가 모두 정수이면 /는 정수 나눗셈을 수행하고 소수 부분을 버립니다. 반올림하는 것이 아니라 0 방향으로 잘라냅니다. 진짜 분수를 원한다면 적어도 한쪽 피연산자가 부동소수점이어야 합니다.
% 연산자는 정수에서만 동작합니다. double에 %를 쓰면 컴파일 오류가 납니다. 부동소수점의 나머지는 <cmath>의 std::fmod를 사용하세요.
대입과 복합 연산자
단독 =는 대입하며, 비교하지 않습니다. C++는 변수 이름을 반복하지 않도록 연산과 대입을 결합한 복합 형태를 제공합니다.
전형적이고 뼈아픈 실수는 조건문 안에서 ==를 쓰려다 =를 쓰는 것입니다. if (x = 0)은 비교하는 대신 x에 0을 대입한 뒤 그 결과(거짓입니다)를 검사합니다. 최신 컴파일러는 경고를 켜면 이를 알려 줍니다. -Wall을 켜 두고 그 경고를 진지하게 받아들이세요.
비교 연산자
비교 연산자는 예/아니오 질문을 던지고 bool(true 또는 false)을 만들어 냅니다.
기본적으로 cout은 bool을 1 또는 0으로 출력합니다. boolalpha를 한 번 스트림에 넣으면 그 스트림의 나머지 동안 true/false라는 단어로 바뀝니다.
미묘한 함정 하나: 1 < x < 10처럼 비교를 연쇄하지 마세요. 이는 (1 < x) < 10으로 해석됩니다. 첫 번째 비교가 bool(0 또는 1)을 만들고, 그것이 10과 비교되므로 전체는 거의 항상 true가 됩니다. 대신 1 < x && x < 10이라고 쓰세요.
논리 연산자와 단축 평가
&&(그리고), ||(또는), !(부정)는 불리언 식을 결합합니다. 앞의 두 개는 단축 평가를 합니다. 결과가 정해지는 즉시 평가를 멈춥니다.
check("A")도 check("B")도 결코 실행되지 않습니다. 이것이 단축 평가입니다. 이는 단순한 최적화가 아니라 하나의 도구입니다. ptr->ready 부분은 ptr이 null이 아닐 때만 도달하므로, 댕글링 포인터 역참조를 피하면서 if (ptr != nullptr && ptr->ready)를 안전하게 쓸 수 있습니다.
증가와 감소
++는 1을 더하고, --는 1을 뺍니다. 각각 전위 형태와 후위 형태가 있으며, 결과를 사용할 때 그 차이가 중요해집니다.
부수 효과만 원할 때(예를 들어 for 루프에서)는 ++i를 쓰세요. 단순한 int에서는 동일하지만, 이터레이터 같은 무거운 타입에서는 후위 증가가 먼저 이전 값을 복사해야 하므로 낭비되는 작업입니다.
경고 하나 더: 하나의 식에서 같은 변수를 두 번 수정하지 마세요. 예를 들어 i = i++ + 1;이나 arr[i] = i++; 같은 것입니다. 이러한 갱신의 순서는 명시되어 있지 않으며, 결과는 정의되지 않은 동작입니다. 각 변수는 한 문장당 한 번만 수정하도록 하세요.
비트 연산자와 우선순위
저수준 작업을 위해 비트 연산자가 있습니다. &(그리고), |(또는), ^(xor), ~(부정), 그리고 시프트 <<와 >>입니다.
주의: <<와 >>는 cout의 스트림 연산자이기도 합니다. cout 줄 안에서는 보통 비트 시프트를 괄호로 감싸야 합니다. 그렇지 않으면 컴파일러가 그것을 스트림 삽입으로 읽습니다.
마지막으로, 우선순위는 연산자를 섞을 때 무엇이 먼저 묶이는지를 결정합니다. 수학에서처럼 *와 /가 +와 -보다 더 강하게 묶이므로 2 + 3 * 4는 14입니다. 비교는 산술보다 약하게 묶이고, 논리 &&/||는 더 약하게 묶입니다. 헷갈리면 전체 표를 외우지 말고 괄호를 추가하세요. (a + b) * c가 독자가 규칙을 기억하리라 믿는 것보다 명확합니다.
다음: 형 변환
위에서 (double)a / b가 정수를 부동소수점 나눗셈으로 강제하는 것을 보았습니다. 그것이 바로 캐스트입니다. 값을 한 타입에서 다른 타입으로 의도적으로 변환하는 것이죠. 다음 페이지에서는 암시적 승격부터 static_cast까지 C++의 변환 도구와 각각이 언제 안전한지를 다룹니다.
자주 묻는 질문
C++의 연산자에는 어떤 것들이 있나요?
C++는 연산자를 산술(+ - * / %), 비교(== != < > <= >=), 논리(&& || !), 대입(= += -= ...), 증가/감소(++ --), 비트(& | ^ ~ << >>)로 분류합니다. 그 밖에 삼항 연산자 ?:와 sizeof 같은 것들도 몇 가지 있습니다.
C++에서 5 / 2가 왜 2인가요?
두 피연산자가 모두 int이기 때문에 /가 정수 나눗셈을 수행하고 소수 부분은 반올림이 아니라 버려집니다. 2.5를 얻으려면 적어도 한쪽 피연산자를 부동소수점 값으로 만드세요: 5.0 / 2 또는 5 / 2.0.
C++에서 ++i와 i++의 차이는 무엇인가요?
둘 다 i에 1을 더합니다. ++i(전위 증가)는 먼저 증가시킨 뒤 새 값을 반환하고, i++(후위 증가)는 이전 값을 반환한 다음 증가시킵니다. 부수 효과만 필요하다면 ++i를 사용하세요.