2016년 4월 20일 수요일

C/C++ 연산자 우선순위

우선순위연산자설명결합방향
1::Scope resolution (범위 확인)좌 → 우
2++ --후위 증가와 감소
()함수호출
[]배열 첨자
.참조로 요소 선택
->포인터를 통해 요소 선택
3++ --전위 증가와 감소우 → 좌
+ 단항 덧셈, 뺄셈
! ~논리 NOT, 비트단위 NOT
(type)타입 캐스트
*역참조
&주소값
sizeofSize-of 연산자
newnew[]동적 메모리 할당
deletedelete[]동적 메모리 해제
4.*
->*
멤버 접근좌 → 우
5* / %곱셈, 나눗셈, 나머지
6+ 더하기, 빼기
7<< >>비트 왼쪽 쉬프트와 오른쪽 쉬프트
8< <=관계 연산자 < 와 ≤
> >=관계 연산자 > 와 ≥
9== !=관계 = 와 ≠
10&비트 AND
11^비트 XOR (exclusive or)
12|비트 OR (inclusive or)
13&&논리 AND
14||논리 OR
15?:삼항연산자우 → 좌
=직접 할당 (C++ 클래스를 위해 기본 제공)
+= −=합과 차 할당
*= /= %=곱, 몫, 나머지 할당
<<= >>=비트 왼쪽 쉬프트와 오른쪽 쉬프트 후 할당
&= ^= |=비트연산 AND, XOR, OR 연산 후 할당
16throw(예외를 위한)Throw 연산자
17,콤마좌 → 우
숫자가 낮을수록 먼저 계산한다. (연산자 우선순위 표)

연산자 우선순위때문에 코딩미스가 자주 나타나곤 한다. 이제까지 코딩하면서 이런 우선순위때문에 ~얻거나 잃은~ 겪어본 것들을 적어보고자 한다.

1. &&|| 같은 경우에는 좌에서 우로 연산을 살피기 때문에 런타임 오류(divide by zero, out of range 등)을 예상하여 막을 수 있다.
 예를 들면, if(n >= 0 && arr[n] == 1) return 0; 과 같은 코드는 n이 음수일 때는 n >= 0 이 false 이므로 더 이상 조건을 확인하지 않는다. 즉, arr[음수] 인 경우가 없으므로 out of range는 일어나지 않는다. OR연산인 || 역시 중간에 true인 조건이 확인되면 마찬가지로 더 이상 확인하지 않는다.

2. << 과 같은 쉬프트 연산은 사칙연산보다 우선순위가 낮기 때문에 $2^n$ 꼴을 쉬프트로 표현하려면 괄호를 적절히 사용해야한다.
 printf("%d", 1<<2+1); 의 출력값은 5가 아닌 8이다.

3. printf는 우에서 좌로 해석한다. (단, C99에서. 이 링크에 의하면, C++에는 순서가 정의되어 있지 않고 컴파일러의 최적화에 따라 다르다고 한다.) 아래 코드의 출력값을 예상해보자.
main(){
    int a=1, b=8, c=4;
    printf("%d %d %d", ++a+c, b/a, b*c++);
}
결과는 6 4 32 가 아닌 7 8 32이다.
그 이유는 printf 함수는 가변 인자를 받아서 인자의 개수를 모르기 때문이다.

4. 타입 캐스팅은 자료형이 작은 쪽에서 큰 쪽으로 일어난다.
  • int + int = int
  • long long + int = long long
    • ex) long long r = 15 + INT_MAX 는 오버플로우지만, long long r = 15LL + INT_MAX 는 잘 들어간다.
  • int + double = double
    • ex) int를 double로 변환할 때 쓰면 편하다. (int)50+(double)0.0 은 (double)50.0로 변환된다.

5. 연산의 우선순위는 분명 존재한다. 하지만 잘 작동할 수 있다.
격자무늬를 표현하는 r%2^c%2 라는 코드는 XOR(^)의 우선순위가 mod(%)보다 낮음에도 (r%2)^(c%2) 처럼 잘 작동한다.
3*k++ 과 같이 모호한 코드는 작성하지 않는 것이 가장 좋은 방법이다.

마지막으로, 이 모든 내용은 컴파일러마다 다를 수 있다. 그리고 명시적인 표현의 여부에 따라 다를 수 있다.
작성된 코드가 의도와 같게 작동하지 않을 때, '이럴 수도 있다' 정도의 지식으로만 참고하면 좋을 것 같다.

잘못된 내용이 있다면 지적해주시면 감사하겠습니다.


댓글 쓰기

게시글 목록