식은 순서대로 줄어든다
앞 글에서 기호를 하나씩 봤습니다. 이제 여러 개가 한 줄에 섞이면 어떻게 되는지 봅니다.
int r = 3 + 4 * 5;
23 입니다. 왼쪽부터 읽었다면 35 가 나왔을 겁니다.
컴파일러는 한 줄을 왼쪽부터 읽지 않습니다.
식은 우선순위가 높은 곳부터 값으로 바뀌고, 값 하나만 남을 때까지 줄어듭니다.
식은 값 하나로 줄어든다
3 + 4 * 5 가 계산되는 과정을 한 단계씩 보겠습니다.
색이 칠해진 부분이 이번 단계에서 계산되는 곳입니다.
세 번째 것이 앞 글에서 결과가 갈렸던 이유입니다.
double r1 = (double)a / b; // 3.3333... 캐스팅이 먼저
double r2 = (double)(a / b); // 3 나눗셈이 먼저
(double)(a / b) 는 정수로 나눈 뒤에 실수로 옮기니 이미 늦었습니다.
같은 글자로 되어 있는데 괄호 위치 하나가 결과를 바꿉니다.
외우지 말고 괄호를 쓴다
우선순위 표는 이렇습니다. 앞 글에서 본 기호들을 순서대로 놓은 것입니다.
| 순서 | 연산자 |
|---|---|
| 1 | ( ) 괄호 |
| 2 | ! ++ -- (타입) 캐스팅 |
| 3 | * / % |
| 4 | + - |
| 5 | < > <= >= |
| 6 | == != |
| 7 | && |
| 8 | || |
| 9 | = += -= 대입 |
외울 필요 없습니다. 알아둘 건 두 가지뿐입니다.
- 산술이 비교보다 먼저고, 비교가 논리보다 먼저고, 대입이 맨 마지막입니다.
if (a + 1 > b && c)가 괄호 없이 되는 이유입니다 - 나머지는 헷갈리면 괄호를 치세요. 괄호를 친 코드가 진 적은 없습니다
bool r = true || false && false; // 헷갈립니다
bool r = true || (false && false); // 같은 뜻인데 읽힙니다
&& 가 || 보다 먼저라 둘은 같은 식입니다. 그런데 읽는 사람은 그걸 모릅니다.
대입이 맨 마지막인 것도 이제 말이 됩니다.
int r = 3 + 4 * 5; 에서 오른쪽이 값 하나로 다 줄어든 다음에야 r 의 자리에 씁니다.
&& 는 오른쪽을 안 볼 수도 있다
순서 이야기의 마지막입니다. 어떤 부분은 아예 계산되지 않습니다.
왼쪽만으로 결과가 정해지면 오른쪽은 실행되지 않습니다.
&& 는 왼쪽이 거짓이면 오른쪽이 뭐든 거짓입니다. 그래서 안 봅니다.
|| 는 왼쪽이 참이면 오른쪽을 안 봅니다. 단축 평가라고 합니다.
현금이 있고 시간이 되면 간다고 할 때, 현금이 없으면 시간은 확인도 안 하는 것과 같습니다.
이게 단순한 최적화가 아니라 자주 쓰는 안전장치입니다.
null 검사를 먼저 두고 바로 뒤에서 그 객체를 쓸 수 있는 이유가 단축 평가입니다.
순서를 뒤집으면 검사가 있으나 마나입니다.
정리
- 식은 우선순위가 높은 곳부터 값으로 바뀌고, 값 하나만 남을 때까지 줄어든다.
- 괄호는 새 규칙이 아니라 그 순서를 직접 정하는 도구다. 헷갈리면 괄호를 쓴다.
- 산술 → 비교 → 논리 → 대입 순이다. 대입이 맨 마지막이라 오른쪽이 다 줄어든 뒤에 쓴다.
- 캐스팅이 나눗셈보다 늦으면 아무 의미가 없다.
&&와||는 왼쪽으로 결과가 정해지면 오른쪽을 실행하지 않는다.