연산자와 표현식: 계산하고 비교하고 판단하기
변수에 값을 담는 법을 배웠다면, 이제 그 값을 움직일 차례입니다. 숫자를 더하고, 크기를 비교하고, 조건을 묶고, 값을 갱신하는 일은 모두 연산자로 이루어집니다. 그리고 이런 연산자와 값이 합쳐진 덩어리를 표현식(expression) 이라고 부릅니다.
예를 들어 10 + 20도 표현식이고, score >= 60도 표현식이며, count += 1도 표현식입니다. 표현식은 값을 만들어 내기도 하고, 경우에 따라 변수의 값을 바꾸는 부수 효과(side effect) 도 일으킵니다.1
앞으로 배울 조건문과 반복문도 결국은 표현식 위에 서 있습니다. if가 제대로 판단하려면 조건식이 정확해야 하고, while이 멈출지 계속 돌지 결정하는 것도 표현식입니다. 그래서 이번 장은 C++ 문법의 작은 챕터가 아니라, 뒤의 챕터들을 받쳐 주는 중요한 바닥 공사라고 생각하면 됩니다.
연산자와 표현식, 먼저 감 잡기
연산자는 값을 가지고 어떤 일을 시키는 기호입니다. +는 더하고, >는 비교하고, =는 대입합니다. 그리고 연산의 대상이 되는 값이나 변수를 피연산자라고 부릅니다.
예를 들어 다음 코드를 봅시다.
int a = 10;
int b = 3;
int result = a + b;여기서 +는 연산자이고, a와 b는 피연산자입니다. a + b는 표현식이며, 그 결과는 13입니다.
비슷하게 아래도 모두 표현식입니다.
x * y
age >= 20
isReady && hasTicket
count = count + 1마지막 줄은 조금 특별합니다. 값을 계산할 뿐 아니라 count의 값도 바꿉니다. 즉, 표현식은 계산만 하는 것도 있고, 상태를 바꾸는 것도 있다는 점을 기억해 두면 좋습니다.1
산술 연산자: 계산의 기본 체력
가장 먼저 익힐 것은 숫자를 다루는 연산자입니다.
+: 덧셈-: 뺄셈*: 곱셈/: 나눗셈%: 나머지
예제를 보면 훨씬 익숙해집니다.
#include <iostream>
using namespace std;
int main() {
int a = 8;
int b = 3;
cout << "a + b = " << a + b << 'n';
cout << "a - b = " << a - b << 'n';
cout << "a * b = " << a * b << 'n';
cout << "a / b = " << a / b << 'n';
cout << "a % b = " << a % b << 'n';
}실행하면 a / b는 2, a % b는 2가 됩니다.
여기서 초보자가 가장 많이 놀라는 지점은 정수 나눗셈입니다. 8 / 3은 수학적으로는 2.666...이지만, 두 값이 모두 int라면 C++은 소수점 아래를 버리고 2만 남깁니다. 소수 결과가 필요하면 적어도 한쪽을 실수형으로 만들어야 합니다.2
cout << 8 / 3 << 'n'; // 2
cout << 8.0 / 3 << 'n'; // 2.66667...또 하나, %는 나머지를 구하는 연산자입니다. 초보 단계에서는 정수끼리 사용할 때 가장 자연스럽게 이해하면 됩니다.2
cout << 10 % 3 << 'n'; // 1
cout << 17 % 5 << 'n'; // 2이 나머지 연산은 꽤 유용합니다. 예를 들어 짝수인지 확인할 때 number % 2 == 0처럼 자주 씁니다.
대입 연산자: 값을 넣고, 누적해서 바꾸기
대입 연산자는 오른쪽의 값을 왼쪽 변수에 넣습니다.
int score;
score = 90;이건 아주 단순해 보이지만, 프로그램에서는 정말 자주 나옵니다. 값은 한 번 넣고 끝나는 것이 아니라 계속 갱신되기 때문입니다.
그래서 C++은 복합 대입 연산자도 제공합니다.
+=-=*=/=%=
이 연산자들은 코드를 짧고 읽기 좋게 만들어 줍니다.
int x = 10;
x += 5; // x = x + 5
x -= 2; // x = x - 2
x *= 3; // x = x * 3예를 들어 점수를 누적할 때는 이렇게 씁니다.
int total = 0;
total += 30;
total += 40;
total += 20;대입은 단순한 문장 같지만, C++에서는 대입 자체도 표현식으로 취급됩니다. 그래서 연쇄 대입도 가능합니다.3
int a, b, c;
a = b = c = 0;이 코드는 오른쪽부터 해석되어 c에 0을 넣고, 그 결과를 b, 다시 a에 넣습니다.3
하지만 여기서 아주 중요한 함정이 하나 있습니다.
if (x = 10) {
cout << "실행됨n";
}이 코드는 x가 10인가?를 묻는 게 아닙니다. x에 10을 대입하는 코드입니다. 비교하려면 =가 아니라 ==를 써야 합니다.
if (x == 10) {
cout << "x는 10입니다.n";
}초보자에게 =와 ==는 닮은 얼굴의 쌍둥이 같은 존재입니다. 하나는 넣기, 하나는 비교하기입니다. 헷갈리면 프로그램이 아주 태연하게 엉뚱한 행동을 합니다.
증감 연산자: 1씩 올리고 내리기
값을 딱 1만 늘리거나 줄이고 싶을 때는 증감 연산자를 씁니다.
++: 1 증가--: 1 감소
int n = 5;
++n; // n은 6
--n; // n은 5그런데 증감 연산자는 조금 장난기 있는 친구입니다. 앞에 붙느냐 뒤에 붙느냐에 따라 표현식의 결과가 달라집니다.
int x = 5;
int a = ++x; // x를 먼저 6으로 만든 뒤, a에 6 저장
int y = 5;
int b = y++; // b에 5 저장한 뒤, y를 6으로 증가즉,
++x: 먼저 증가, 그다음 사용x++: 먼저 사용, 그다음 증가
이 차이는 식 안에서 특히 중요합니다.3
int i = 3;
cout << ++i << 'n'; // 4 출력
int j = 3;
cout << j++ << 'n'; // 3 출력
cout << j << 'n'; // 그 다음에는 4반복문에서 자주 보게 될 i++도 바로 이 증감 연산자입니다. 다만 초보자라면 한 식 안에서 증감 연산자를 여러 번 섞는 코드는 피하는 것이 좋습니다.
// 이렇게 쓰지 마세요
int result = i++ + ++i;이런 식은 단순히 읽기 어려운 정도가 아니라, 정의되지 않은 동작(undefined behavior) 을 만들 수 있습니다.2 즉, 컴파일러나 환경에 따라 결과가 달라질 수 있고, 아예 이상하게 동작해도 언어가 보장하지 않습니다.
그래서 증감 연산이 들어가면 더더욱 욕심내지 않는 편이 좋습니다. 차라리 한 줄씩 나누세요.
++i;
int result = i + i;또는 정말 의도한 계산을 더 분명하게 적는 편이 훨씬 안전합니다.
비교 연산자: 참과 거짓을 만드는 도구
비교 연산자는 두 값을 비교해서 true 또는 false를 만듭니다. C++에서는 이 결과가 bool 값으로 다뤄집니다.
==: 같다!=: 다르다>: 크다<: 작다>=: 크거나 같다<=: 작거나 같다
예를 들어:
int score = 75;
cout << (score >= 60) << 'n'; // 1
cout << (score == 100) << 'n'; // 0출력에서는 true가 1, false가 0으로 보일 수 있습니다. 지금은 그렇게 나오는구나 하고 받아들이면 충분합니다.
비교 연산자는 조건문과 반복문의 핵심 재료입니다.
int age = 20;
bool isAdult = age >= 19;여기서 age >= 19가 바로 판단의 재료가 되는 표현식입니다.
초보자가 자주 빠지는 함정도 하나 있습니다.
int a = 3;
int b = 5;
int c = 7;
cout << (a < b < c) << 'n';이 코드는 "a가 b보다 작고, b가 c보다 작은가?"라는 뜻이 아닙니다. C++은 이를 왼쪽부터 묶어서 (a < b) < c처럼 해석합니다.6 먼저 a < b는 true가 되고, 그다음 true < c를 비교하게 됩니다. 사람이 읽는 뜻과 프로그램이 읽는 뜻이 달라지는 순간입니다.
범위를 확인하고 싶다면 이렇게 써야 합니다.
cout << ((a < b) && (b < c)) << 'n';논리 연산자: 조건을 이어 붙이기
비교 연산자가 조건 하나를 만들었다면, 논리 연산자는 조건 여러 개를 연결합니다.
&&: 그리고(AND)||: 또는(OR)!: 아니다(NOT)
예제를 보겠습니다.
int age = 20;
bool hasID = true;
cout << ((age >= 19) && hasID) << 'n';
cout << ((age < 19) || hasID) << 'n';
cout << !(age >= 19) << 'n';논리 연산자는 보통 비교 연산자와 함께 등장합니다.
int score = 82;
int attendance = 95;
bool passed = (score >= 60) && (attendance >= 80);이 표현식은 "점수도 기준 이상이고, 출석도 기준 이상이면 통과"라는 뜻입니다.
그리고 논리 연산자에는 아주 중요한 특징이 있습니다. 바로 단축 평가(short-circuit evaluation) 입니다.4
&&는 왼쪽이 false면 오른쪽을 굳이 보지 않습니다. 어차피 전체 결과가 false이기 때문입니다. ||는 왼쪽이 true면 오른쪽을 보지 않습니다. 이미 전체 결과가 true로 결정났기 때문입니다.4
예를 들어:
int x = 0;
int y = 10;
bool result = (x != 0) && (y / x > 2);여기서 왼쪽 x != 0이 false이므로, 오른쪽 y / x > 2는 평가하지 않습니다. 덕분에 0으로 나누는 위험한 상황을 피할 수 있습니다. 이런 이유로 논리식에서는 안전한 검사를 왼쪽에 두는 습관이 중요합니다.
우선순위와 결합 방향: 식은 왜 그렇게 계산될까?
이제 중요한 질문이 생깁니다.
int result = 2 + 3 * 4;이 식은 20일까요, 14일까요?
정답은 14입니다. 곱셈 *이 덧셈 +보다 먼저 결합되기 때문입니다. 이런 규칙을 연산자 우선순위라고 합니다.5
그렇다면 아래는 어떨까요?
int result = 10 - 3 - 2;-와 -는 우선순위가 같습니다. 이럴 때는 결합 방향(associativity) 이 작동합니다. 뺄셈은 왼쪽에서 오른쪽으로 묶이므로 (10 - 3) - 2, 즉 5가 됩니다.5
반대로 대입은 오른쪽에서 왼쪽으로 묶입니다.
a = b = 5;이 코드는 a = (b = 5)로 해석됩니다.5
초보 단계에서 꼭 기억하면 좋은 우선순위 순서는 다음 정도입니다.
괄호
()단항 연산자
++x,--x,!x*,/,%+,-<,<=,>,>===,!=&&||=,+=,-=등 대입 연산자
예를 들어:
bool ok = 10 > 5 && 3 < 1 || 7 == 7;이 식은 대충 읽으면 헷갈립니다. 하지만 우선순위에 따라 다음처럼 묶입니다.
bool ok = ((10 > 5) && (3 < 1)) || (7 == 7);결과는 true입니다.
다만 여기서 중요한 교훈은 하나입니다. 우선순위를 외우는 것보다 괄호를 잘 쓰는 것이 더 실용적이라는 점입니다. 실제 코드에서는 컴파일러보다 동료와 미래의 나를 설득해야 합니다.
bool ok = ((score >= 60) && (attendance >= 80)) || isSpecialCase;이 코드는 길어 보이지만, 읽는 사람 입장에서는 훨씬 친절합니다.
또 하나 알아둘 점은, 우선순위와 결합 방향은 식이 어떻게 묶이는지를 설명하는 규칙이지, 모든 세부 계산이 실제로 어떤 순서로 실행되는지를 단순하게 설명하는 개념은 아니라는 것입니다.5 초보 단계에서는 "복잡하면 괄호를 쓰자"만 기억해도 충분합니다.
표현식이 조건문과 반복문의 뼈대가 되는 이유
다음 장에서 if, while, for를 배우게 되는데, 사실 그들의 핵심은 문법보다 표현식의 정확성입니다.
예를 들어 이런 조건이 있다고 합시다.
if ((score >= 60) && (absent < 3)) {
cout << "통과n";
}이 문장에서 진짜 판단을 하는 것은 if라는 단어가 아니라, 괄호 안의 표현식입니다.
반복문도 마찬가지입니다.
while (count < 10) {
count++;
}여기서 반복을 계속할지 멈출지는 count < 10이 결정합니다. 그리고 루프가 앞으로 나아가게 만드는 힘은 count++입니다.
즉,
비교 연산자는 상태를 판단하고
논리 연산자는 조건을 조합하고
대입과 증감 연산자는 상태를 바꾸며
우선순위는 이 모든 것을 올바르게 묶어 줍니다
이 네 가지가 함께 돌아가야 조건문과 반복문이 제대로 작동합니다.
자주 하는 실수 다섯 가지
처음 배울 때 특히 많이 만나는 실수를 모아 보면 이렇습니다.
=와==를 혼동한다정수 나눗셈 결과가 소수일 거라고 기대한다
a < b < c를 범위 비교처럼 쓴다증감 연산을 한 식에 몰아 넣는다
복잡한 식을 괄호 없이 써서 의도를 흐린다
예제를 한 번에 보면 더 분명합니다.
int x = 5;
if (x = 10) { // 비교가 아니라 대입
cout << x; // 10
}
cout << 5 / 2; // 2
cout << (1 < 2 < 3); // 사람이 기대한 의미와 다름이런 실수는 문법을 몰라서라기보다, 표현식을 너무 쉽게 본 탓에 생깁니다. 연산자는 기호 몇 개뿐이지만, 프로그램의 판단력은 거기에 달려 있습니다.
작은 실전 예제: 성적 판정 프로그램
지금까지 배운 연산자를 한 번에 묶어 보겠습니다.
#include <iostream>
using namespace std;
int main() {
int kor = 80;
int eng = 65;
int math = 90;
int total = kor + eng + math;
double average = total / 3.0;
bool passed = (average >= 70.0) && (kor >= 40) && (eng >= 40) && (math >= 40);
cout << "총점: " << total << 'n';
cout << "평균: " << average << 'n';
cout << "합격 여부: " << passed << 'n';
int studentCount = 0;
studentCount += 1;
cout << "처리한 학생 수: " << studentCount << 'n';
}이 짧은 코드 안에도 핵심이 모두 들어 있습니다.
kor + eng + math는 산술 표현식이고, total / 3.0은 실수 나눗셈을 사용한 평균 계산입니다. average >= 70.0과 각 과목의 과락 검사들은 비교 표현식이며, 이것들을 &&로 묶어 최종 합격 여부를 만듭니다. 마지막의 studentCount += 1은 복합 대입 연산자의 예입니다.
이런 식을 자연스럽게 읽을 수 있다면, 다음 장의 조건문과 반복문은 훨씬 편안하게 느껴질 것입니다.
한눈에 정리
연산자는 프로그램이 값을 다루는 방법이고, 표현식은 그 연산이 실제로 이루어지는 문장 조각입니다. +, -, *, /, %는 계산을 맡고, ==, !=, <, >는 비교를 맡습니다. &&, ||, !는 조건을 조합하고, =, +=, ++는 값을 바꿉니다.
그리고 가장 중요한 규칙은 이것입니다. 조건문과 반복문은 결국 표현식의 정확성에 달려 있다.
식이 정확하면 프로그램은 똑똑하게 판단합니다. 식이 흐리면 프로그램은 아주 성실하게 틀립니다.
다음 장에서는 이 표현식들이 실제로 if, switch, while, for 안에서 어떻게 흐름을 제어하는지 배워 보겠습니다.
참고
1Expressions - cppreference.com
2Increment/decrement operators - cppreference.com
이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.