연산자(operator)는 값을 셈하는 기호이고, 우선순위(precedence)는 한 식에 연산자가 여럿일 때 무엇부터 묶어 셈할지 정한 차례입니다. 왼쪽부터 차례로 셈하거나 &&·||의 오른쪽까지 늘 셈한다고 보면 출력이 틀리므로, 식을 계산 차례대로 하나씩 값으로 접어 가야 합니다. 아래 그림은 한 줄씩 실행하며 칸에 드는 값과, &&·||가 오른쪽을 셈하지 않는 줄을 보입니다.
1산술·관계·논리 연산자
그림의 코드입니다.
#include <stdio.h>
int main(void) {
int a = 7;
int b = 0;
int r = a + 2 * 3 % 4;
int t = b > 0 && a % b == 1;
int s = a > 3 || b == 1;
printf("%d %d %d\n", r, t, s);
return 0;
}
9 0 1| 갈래 | 연산자 | 결과 |
|---|---|---|
| 산술 | + - * / % | 수 |
| 관계 | < <= > >= == != | 참이면 1, 거짓이면 0 |
| 논리 | &&(그리고) ||(또는) !(아님) | 참이면 1, 거짓이면 0 |
논리 연산자는 0을 거짓, 0이 아닌 값을 모두 참으로 보아 !5는 0, !0은 1입니다.
2우선순위와 결합 방향
연산자가 셈하는 값을 피연산자(operand)라 합니다. a + b에서는 a와 b입니다. !a·-a처럼 피연산자 하나에 붙는 연산자를 단항 연산자(unary operator)라 합니다.
위 칸일수록 먼저 묶어 셈합니다.
| 순위 | 연산자 |
|---|---|
| 높음 | 괄호 () · 단항 ! ++ -- 부호 - 캐스팅 |
* / % | |
+ - | |
시프트 << >> | |
관계 < <= > >= | |
같음 == != | |
비트 & → ^ → | | |
논리 && → || | |
조건 ? : | |
| 낮음 | 대입 = += 등 |
같은 순위끼리 어느 쪽부터 묶는지를 결합 방향(associativity)이라 하고, 대부분은 왼쪽부터 묶습니다. 6번 줄은 *와 %가 같은 순위라 2 * 3을 먼저 셈하고, 그 값으로 6 % 4를, 마지막으로 7 + 2를 셈합니다.
단항·조건·대입만 오른쪽부터 묶습니다. a = b = 4;는 b에 4를 넣고 그 값을 a에 넣습니다.
시프트·비트 연산은 비트 연산과 2의 보수에서 셈합니다.
3단락 평가와 =·==
&&는 왼쪽이 0이면, ||는 왼쪽이 0이 아니면 결과가 정해져 오른쪽을 셈하지 않습니다. 이것을 단락 평가(short-circuit evaluation)라 하고, 자료에 따라 단축 평가라고도 부릅니다.
7번 줄은 b가 0이라 오른쪽 a % b를 셈하지 않아 0으로 나누지 않습니다. 오른쪽에 칸을 바꾸는 식이 있어도 실행되지 않습니다.
=는 오른쪽 값을 왼쪽 칸에 넣는 대입이고, 같은지 묻는 비교는 ==입니다. 절차형 SQL의 구조에서 본 PL/SQL과 기호가 뒤집혀 있습니다.
| 뜻 | C·Java·Python | PL/SQL |
|---|---|---|
| 칸에 값을 넣음(대입) | = | := |
| 같은지 물음(비교) | == | = |
예제
나머지·몫·관계·논리가 한 식에 섞였습니다. 무엇이 찍힐까요?
#include <stdio.h>
int main(void) {
int a = 10;
int b = 3;
int c = 0;
int x = a % b * 2 + a / b;
int y = !c && a - b * 3 > 0;
int z = c == 0 || a / c > 1;
printf("%d %d %d\n", x, y, z);
return 0;
}
실행 결과 보기
5 1 1x는 a % b(1)에 2를 곱한 2와 a / b(3)를 더해 5입니다. y는 !c가 1이고 a - b * 3이 1이라 1 > 0이 1, 둘 다 참이라 1입니다. z는 c == 0이 1이라 오른쪽 a / c를 셈하지 않고 1입니다.
같은 코드를 Java·Python으로
| C | Java | Python | |
|---|---|---|---|
| 논리 연산자 | && || ! | && || ! | and or not |
| 비교의 결과 | 1 · 0 | true · false | True · False |
| 정수에 논리 연산 | 5 && 3 → 1 | 컴파일 오류 | 5 and 3 → 3 |
| 몫 · 거듭제곱 | 7 / 2 → 3 · 거듭제곱 연산자 없음 | C와 같음 | 7 // 2 → 3 · 2 ** 3 → 8 |
Python의 and·or는 1·0이 아니라 마지막으로 본 값을 돌려줍니다. 0 or 5는 5입니다.
시험 포인트
- 필기: 우선순위는 단항 → 산술(
*/%→+-) → 시프트 → 관계 → 같음 → 비트 → 논리(&&→||) → 조건 → 대입 차례입니다. 같은 순위는 왼쪽부터 묶고, 단항·조건·대입은 오른쪽부터 묶습니다. 관계·논리 연산의 결과는 1·0이고,&&·||는 단락 평가를 합니다.%는 정수의 나머지입니다. 대입=·비교==는 PL/SQL의:=·=와 뒤집힌 짝입니다. - 실기: 결과를 그대로 씁니다(연산자 우선순위 출제 주제). 건너뛴 오른쪽 식이 함정입니다. 이름으로는 단락 평가(Short-Circuit Evaluation)를 씁니다.
확인 문제
1. C 연산자를 우선순위가 높은 것부터 늘어놓은 것을 고르세요.
- ①
*→+→<→&&→= - ②
+→*→<→&&→= - ③
*→+→&&→<→= - ④
=→*→+→<→&&
정답
①입니다. 산술 안에서는 곱셈 쪽이 먼저이고, 산술 → 관계 → 논리 → 대입 차례입니다.
- ②
+는*보다 늦습니다. - ③ 관계
<가 논리&&보다 먼저입니다. - ④ 대입
=는 가장 늦습니다.
2. 위 예제에서 6번 줄만 int c = 2;로 바꿨습니다. 출력을 그대로 쓰세요. 답을 끝까지 쓴 뒤 정답을 펼치세요.
정답
받는 답:
5 0 1
x는 c와 상관없어 5 그대로입니다. y는 !c가 0이라 &&의 오른쪽을 셈하지 않고 0입니다. z는 c == 0이 0이라 이번에는 오른쪽 a / c > 1을 셈합니다. 10 / 2는 5라 참이 되어 1입니다.
- 틀리는 꼴 y
1:!2를 1로 읽었습니다. 2는 참이라!2는 0입니다. - 틀리는 꼴 z
0: 왼쪽이 거짓이면||가 바로 0을 낸다고 봤습니다.||는 오른쪽까지 보아 하나라도 참이면 1입니다.
3. 다음 코드에서 식이 접히는 차례를 적은 표의 (가)~(라)를 채운 뒤, 출력을 그대로 쓰세요. 답을 끝까지 쓴 뒤 정답을 펼치세요.
#include <stdio.h>
int main(void) {
int p = 3;
int q = 8;
int k = p + q * 2 > 18;
int w = q % p == 2 && !k;
printf("%d %d\n", k, w);
return 0;
}
| 줄 | 접히는 차례 | 칸 |
|---|---|---|
| 6 | q * 2 → 16 · 3 + 16 → 19 · 19 > 18 → (가) | k = (가) |
| 7 | q % p → (나) · 그 값 == 2 → 1 · !k → (다) | w = (라) |
정답
(가) 1 · (나) 2 · (다) 0 · (라) 0
받는 답:
1 07번 줄은 &&의 왼쪽이 1이라 오른쪽 !k까지 셈합니다. k가 1이라 !k는 0이고, 1 && 0은 0입니다.
- 틀리는 꼴 (가)에 19: 비교를 놓쳤습니다.
> 18까지 셈해야 하고, 관계 연산의 결과는 1 또는 0입니다. - 틀리는 꼴 (라)에 1:
!k를 k로 읽었습니다.!는 참과 거짓을 뒤집습니다.
4. 다음 코드의 출력을 그대로 쓰세요. 답을 끝까지 쓴 뒤 정답을 펼치세요.
#include <stdio.h>
int main(void) {
int n = 17;
int m = 5;
int z = 0;
char g = 'A';
int p = n / m * m + n % m;
int t = g + 2 == 'C' || n / z;
int u = m > 9 && n / z;
printf("%d %d %d\n", p, t, u);
return 0;
}
정답
받는 답:
17 1 0p는 몫 3에 5를 곱한 15와 나머지 2를 더해 17입니다. t는 +가 ==보다 먼저라 65 + 2 = 67이 'C'의 번호 67과 같아 1이고, ||의 오른쪽 n / z는 셈하지 않습니다. u는 m > 9가 0이라 오른쪽을 셈하지 않고 0입니다. 두 줄 모두 0으로 나누는 식에 닿지 않습니다.
- 틀리는 꼴
오류로 멈춤:n / z를 셈한다고 봤습니다. 왼쪽만으로 결과가 정해져 오른쪽은 실행되지 않습니다. - 틀리는 꼴 p
2:n / m * m을n / (m * m)으로 읽었습니다./와*는 같은 순위라 왼쪽부터 묶습니다.
5. 다음 (가)~(다)를 쓰세요. (가)는 한글 이름과 영문으로, (나)·(다)는 기호로 쓰세요. 답을 끝까지 쓴 뒤 정답을 펼치세요.
- (가)
&&의 왼쪽이 0이거나||의 왼쪽이 0이 아니면, 오른쪽은 셈하지 않고 결과를 내는 방식의 이름입니다. - (나) C에서 두 값이 같은지 묻는 연산자입니다.
- (다) PL/SQL에서 변수에 값을 넣는 기호입니다.
정답
받는 답: (가) 단락 평가(Short-Circuit Evaluation) 또는 단축 평가 · (나) == · (다) :=
(가)의 실마리는 '왼쪽만 보고 결과를 정한다'입니다. (나)·(다)는 C와 PL/SQL에서 기호가 뒤집힌 짝입니다.
- 틀리는 꼴
=— (나)에: C에서=는 대입입니다. 같은지 묻는 것은==입니다. - 틀리는 꼴
=— (다)에: PL/SQL에서=는 같은지 묻는 비교입니다. 값을 넣는 것은:=입니다.
처음이라면: C 언어 과목(준비 중)의 '비교와 참 · 거짓' · '&& · || · !와 단락 평가' 레슨에서 같은 규칙을 처음부터 천천히 봅니다.