이번 문서의 목표: 이 파일을 다 읽으면 C의 주요 연산자를 우선순위·결합 방향에 따라 분류하고, 복잡한 표현식을 한 줄씩 손으로 계산해 정확한 출력값을 예측할 수 있다.
왜 연산자 우선순위까지 외워야 하는가
독학사 C프로그래밍 시험에서 가장 흔한 문제 유형 중 하나가 “다음 코드의 출력 결과는?”이다. 이런 문제는 문법을 아는 것만으로는 풀리지 않는다. a + b * c가 왜 (a + b) * c가 아니라 a + (b * c)로 계산되는지, a++와 ++a가 왜 다른 값을 만드는지를 연산자의 우선순위(precedence)와 결합 방향(associativity)을 근거로 한 줄씩 계산할 수 있어야 한다. 이번 편은 그 계산 규칙을 정리하고, 실제로 자주 틀리는 표현식을 손코딩으로 추적한다.
쉽게 말하면: 연산자 우선순위는 여러 연산이 뒤섞인 문장에서 “어느 것부터 계산할지” 정해 놓은 신호등 규칙이다. 신호를 무시하고 왼쪽부터 순서대로 계산하면 엉뚱한 답이 나온다.
연산자의 분류와 우선순위
C의 주요 연산자를 우선순위가 높은 것부터 낮은 것 순서로 정리하면 다음과 같다(같은 행은 우선순위가 같음을 의미하며, 실제 결합 방향에 따라 계산 순서가 정해진다).
| 우선순위(높음 → 낮음) | 연산자 | 이름 | 결합 방향 |
|---|---|---|---|
| 1 | () [] | 함수 호출, 배열 첨자 | 왼쪽에서 오른쪽 |
| 2 | ++ -- (전위), !, 단항 - | 단항 연산자(unary operator) | 오른쪽에서 왼쪽 |
| 3 | * / % | 곱셈, 나눗셈, 나머지 | 왼쪽에서 오른쪽 |
| 4 | + - | 덧셈, 뺄셈 | 왼쪽에서 오른쪽 |
| 5 | < <= > >= | 관계 연산자(비교) | 왼쪽에서 오른쪽 |
| 6 | == != | 등가 연산자(비교) | 왼쪽에서 오른쪽 |
| 7 | && | 논리 AND | 왼쪽에서 오른쪽 |
| 8 | || | 논리 OR | 왼쪽에서 오른쪽 |
| 9 | = += -= *= /= | 대입, 복합 대입 | 오른쪽에서 왼쪽 |
이 표에서 시험에 자주 나오는 순서는 곱셈·나눗셈·나머지(3)가 덧셈·뺄셈(4)보다 먼저 계산되고, 관계·등가 비교(5, 6)가 논리 연산(7, 8)보다 먼저 계산되며, 대입(9)이 가장 마지막에 계산된다는 점이다.
#include <stdio.h>
int main(void) {
int a = 2, b = 3, c = 4;
int result = a + b * c > 10 && c - a == 2;
printf("result=%d\n", result);
return 0;
}result=1이 표현식을 우선순위에 따라 한 단계씩 풀어 보면 다음과 같다.
| 단계 | 계산 | 결과 |
|---|---|---|
| 1 | b * c (곱셈이 가장 먼저) | 3 * 4 = 12 |
| 2 | a + 12 (덧셈) | 2 + 12 = 14 |
| 3 | 14 > 10 (관계 연산자) | 1(참) |
| 4 | c - a (뺄셈) | 4 - 2 = 2 |
| 5 | 2 == 2 (등가 연산자) | 1(참) |
| 6 | 1 && 1 (논리 AND) | 1(참) |
| 7 | result = 1 (대입, 마지막) | result에 1 저장 |
C에서 참(true)은 0이 아닌 값, 거짓(false)은 0으로 표현되며, 관계·등가·논리 연산자의 결과값 자체는 항상 1(참) 또는 0(거짓)의 int 값이다.
증감 연산자: 전위와 후위의 차이
++와 --는 변수의 값을 1 증가·감소시키는 증감 연산자(increment/decrement operator)다. 변수 앞에 붙이는 전위(prefix)와 뒤에 붙이는 후위(postfix)는 “언제 값을 증가시키는가”가 다르다.
- 전위
++a: 먼저a의 값을 1 증가시킨 뒤, 그 증가된 값을 표현식의 결과로 사용한다. - 후위
a++: 먼저a의 원래 값을 표현식의 결과로 사용한 뒤, 그다음에a를 1 증가시킨다.
#include <stdio.h>
int main(void) {
int a = 5;
int b = ++a; /* 전위: a를 먼저 6으로 만들고, 그 값을 b에 대입 */
int c = 5;
int d = c++; /* 후위: c의 원래 값 5를 먼저 d에 대입한 뒤 c를 6으로 증가 */
printf("a=%d, b=%d\n", a, b);
printf("c=%d, d=%d\n", c, d);
return 0;
}a=6, b=6
c=6, d=5| 변수 | 값 | 이유 |
|---|---|---|
a | 6 | ++a가 실행되며 a 자체가 6으로 증가 |
b | 6 | 전위 연산이므로 증가된 후의 값(6)이 대입에 쓰임 |
c | 6 | c++가 실행되며 c 자체는 결국 6으로 증가 |
d | 5 | 후위 연산이므로 증가되기 전의 값(5)이 먼저 대입에 쓰이고, 그다음에 c가 증가 |
자주 틀리는 점: “결국
c도 6이 되니까d도 6 아닌가”라고 착각하기 쉽다. 하지만d = c++에서 대입에 쓰이는 값은 증가되기 이전의 값이다.c자체가 최종적으로 6이 되는 것과,d에 어떤 값이 저장되는지는 별개의 문제다. 이 둘을 구분하는 것이 후위 연산자 문제의 핵심이다.
복합 대입 연산자
+=, -=, *=, /=, %= 같은 복합 대입 연산자(compound assignment operator)는 a = a + b를 a += b처럼 줄여 쓰는 문법이다.
#include <stdio.h>
int main(void) {
int score = 70;
score += 10; /* score = score + 10 과 동일 */
score -= 5; /* score = score - 5 와 동일 */
score *= 2; /* score = score * 2 와 동일 */
printf("score=%d\n", score);
return 0;
}score=150| 단계 | 연산 | score 값 |
|---|---|---|
| 시작 | - | 70 |
score += 10 | 70 + 10 | 80 |
score -= 5 | 80 - 5 | 75 |
score *= 2 | 75 * 2 | 150 |
복합 대입 연산자는 단순히 타자 수를 줄이는 것뿐 아니라, 왼쪽에 있는 변수 이름을 한 번만 평가한다는 의미도 있다(배열 첨자처럼 계산 비용이 있는 표현식을 왼쪽에 쓸 때 특히 중요하지만, 독학사 2단계 범위에서는 “같은 의미의 축약형”이라는 점만 정확히 알면 충분하다).
나머지 연산자와 정수 나눗셈의 부호
%(나머지, modulo) 연산자는 정수형에만 사용할 수 있으며, 나눈 나머지를 반환한다. 음수가 포함될 때 결과의 부호는 시험에서 자주 헷갈리는 부분이다.
#include <stdio.h>
int main(void) {
printf("7 %% 3 = %d\n", 7 % 3);
printf("-7 %% 3 = %d\n", -7 % 3);
printf("7 %% -3 = %d\n", 7 % -3);
return 0;
}7 % 3 = 1
-7 % 3 = -1
7 % -3 = 1C99 표준 이후 정수 나눗셈과 나머지 연산의 결과는 0을 향해 버림(truncation toward zero)하는 방식으로 정해져 있다. 즉 나머지의 부호는 나누어지는 수(피제수, dividend)의 부호를 따라간다. -7 % 3은 -7을 3으로 나눈 몫을 0 쪽으로 버림한 -2로 두고, -7 - (3 * -2) = -7 + 6 = -1이 나머지가 된다.
표현식 평가 순서의 함정: 대입과 비교의 혼동
C 시험에서 자주 나오는 실수 중 하나는 등가 비교 연산자 ==와 대입 연산자 =를 헷갈리는 경우다. 이는 문법적으로 완전히 다른 두 연산자이지만, 조건문 안에 잘못 쓰면 컴파일 오류 없이 실행되어 논리 오류를 만든다.
#include <stdio.h>
int main(void) {
int x = 0;
if (x = 5) { /* 비교(==)가 아니라 대입(=)이 실행됨 */
printf("참으로 들어옴, x=%d\n", x);
} else {
printf("거짓, x=%d\n", x);
}
return 0;
}참으로 들어옴, x=5if (x = 5)는 x에 5를 대입하는 표현식이며, 이 대입 표현식 전체의 값은 대입된 값인 5가 된다. C에서는 0이 아닌 값을 모두 참으로 취급하므로, 5는 참이 되어 if 문 안쪽이 실행된다. 게다가 이 과정에서 x의 값 자체가 0에서 5로 바뀌어 버린다는 부작용까지 생긴다.
자주 틀리는 점: 이런 코드는 문법적으로 완전히 올바르기 때문에 컴파일러가 경고만 줄 뿐 오류로 막지 않는다. 독학사 시험에서 “이 코드를 실행하면 어떤 결과가 나오는가”를 묻는 문제로 자주 출제되므로,
if조건 안의=을 보면 반드시 “이것이 비교(==)의 오타인지, 의도한 대입인지”부터 확인하는 습관을 들여야 한다.
핵심 정리
- 곱셈·나눗셈·나머지가 덧셈·뺄셈보다 먼저 계산되고, 관계·등가 비교가 논리 연산보다 먼저 계산되며, 대입은 가장 마지막에 계산된다.
- 전위 증감(
++a)은 증가된 값을 표현식 결과로 쓰고, 후위 증감(a++)은 증가되기 이전의 값을 표현식 결과로 쓴다. 변수 자체의 최종값은 둘 다 같다. - 복합 대입 연산자(
+=등)는a = a op b를 줄인 표기다. - 정수 나머지 연산의 부호는 피제수(나누어지는 수)의 부호를 따라간다.
if조건에서=(대입)과==(비교)를 혼동하면 컴파일 오류 없이 실행되어 논리 오류가 발생한다.
마무리 복습
int a = 2, b = 3, c = 4;
int r = a + b * c;int c = 5;
int d = c++;