4. Expressions

하모씨·2021년 9월 29일
0

KNKsummary

목록 보기
1/23

이 글은 작성자가 K.N.King C Programming: A Modern Approach Second Eddtion을 보고 직접 공부한 뒤에 요약한 것입니다.

1. Arithmetic Operators

단항연산자 : +
이항연산자 : + - * / %

negative operand에 / %를 사용하는 것은 Implementation-Defined Behavior이다.
내부 구현 방식에 따라 결과가 달라지는 행동을 Implementation-Defined Behavior라고 한다.
C89와 C99에서는 (a / b) * b + a % b의 값이 언제나 a와 같도록 한다. 그래서 negative operand의 % 연산의 값이 달라진다.
C89에서는 구현(implementation)에 따라 부호의 결과가 다르다.(-9 / 7은 -1도 되고 -2도 될 수 있음. 소수점을 내림을 할 것인지 올림을 할 것인지)
C99에서는 / 는 부호와 상관없이 0과 가까운 쪽이 결과가 되고(-9 / 7 = -1), %는 i % j의 꼴일 때 i의 부호에 따른다.

연산자 우선순위와 결합(Associativity)

수학 연산자의 우선순위

Highest : +   -  (단항)
          *   /   %
Lowest  : +   -  (이항)

i + j * k  는  i + (j * k)   와 동일
 -i * -j   는  (-i) * (-j)   와 동일
+i +j / k  는  (+j) + (j / k)와 동일

우선순위가 같을 때 결합(associativity)이 작동한다.
만약 왼쪽부터 오른쪽으로 그룹화시키는 연산자라면 left associative 연산자라고 부른다.
이항 수학 연산자(*, /, %, +, -)는 모두 left associative이다.

i - j - k   는  (i - j) - k 와 동일
i * j / k   는  (i * j) / k 와 동일

만약 오른쪽부터 왼쪽으로 그룹화시키는 연산자라면 right associative 연산자라고 부른다.
단항 수학 연산자(+, -)는 둘다 right associative이다.

- + i   는  -(+i)  와 동일

2. Assignment Operators

단순(Simple) 대입

v = e

표현식 e를 계산한 후 v에 복사한다.
C에서 대입은 연산자이고, 두 개의 숫자를 더하는 것이 결과를 만들어내듯 대입은 결과를 만들어낸다.
v = e의 값은 대입 이후의 v의 값이다.

Side Effect

대부분 C언어 연산자들은 피연산자들을 수정하지 않지만, 몇몇 연산자는 피연산자들을 수정한다.
단순 계산보다 그 이상을 하는 연산자들은 Side Effect를 가진다고 한다.
단순 대입 연산자인 =는 왼쪽의 피연산자를 수정하는 side effect를 가진 연산자이다.
표현식 i = 0은 0의 결과를 내고, side effect로써 0의 값을 i에 대입한다.

i = j = k = 0;
i = (j = (k = 0));

= 연산자는 right associative이고, 위와 같이 연계해서도 사용가능하다.

Lvalues

대입 연산자는 왼쪽에 있는 피연산자로 lvalue를 필요로 한다. lvalue는 컴퓨터 메모리안에 저장되는 것으로, 계산의 결과나 상수를 의미하는 것이 아니다. 변수들은 lvalue이다. 10, 2*i 와 같은 표현식은 lvalue가 아니다.

Compound Assignment

C언어는 복합 대입(compound assignment) 연산자를 제공한다.

i += 2;   // i = i + 2와결과가 같은 복합 대입 연산
v += e와 v = v + e는 같지 않다.

위의 사실에 유념해야 한다. 하나의 문제는 연산자 우선순위이다.

i *= j + k 는 i = i * j + k와 같지 않다.

v += e가 v = v + e와 다른 이유는 v가 스스로 side effect를 가지기 때문이다.
이에 대한 건 후에 설명하겠다.
복합 대입 연산자는 = 연산자와 같은 특성을 가진다. 특히 이 연산자는 right associative이다.

3. Increment and Decrement Operators

C언어에는 1을 더하는 ++ 증가연산자와 1을 빼는 -- 감소연산자가 있다.
전위(prefix) 연산자나 후위(postfix) 연산자로 쓰일 수 있다.
피연산자의 값을 수정하는 side effect를 가지고 있다.
++와 --는 단항 연산자인 +와 -보다 높은 우선순위를 가졌고, right associative이다.

i = 1;
printf("%d \n", ++i);
printf("%d \n", i);
Output
2
2
i = 1;
printf("%d \n", i++);
printf("%d \n", i);
Output
1
2

4. Expression Evaluation

우선순위		이름					  Symbols			Associativity
1 		increment(postfix)			++					left
		decrement(postfix)			--					left
2		increment(prefix)			++					right
		decrement(prefix)			--					right
		unary plus					+					right
		unary minus					+					right
3		multiplicative				* / %				left
4		additive					+ -					left
5		assignment			 = *= /= %= += -=			right
예시
a = b += c++ - d + --e / -f
a = b += (c++) - d + --e / -f
a = b += (c++) - d + (--e) / (-f)
a = b += (c++) - d + ((--e) / (-f))
a = b += (((c++) - d) + ((--e) / (-f)))
a = (b += (((c++) - d) + ((--e) / (-f))))
(a = b += (((c++) - d) + ((--e) / (-f)))))

하위 표현식(Subexpression) 계산의 순서

C언어는 하위 표현식의 계산 순서를 정의하지 않는다. 하위 표현식의 값은 보통 계산 순서에 상관없을 때가 많지만, 하위표현식이 피연산자 중 하나를 수정했을 때에는 그렇지 않다.

a = 5;
c = (b = a + 2) - (a = 1);

두번째 구문은 정의되지 않았다(Undefined). 이런 정의되지 않은 행동을 Undefined Behavior라고 한다. C언어 표준은 이것이 어떤 일을 발생시킬 지 말해주지 않는다.

i = 2;
j = i * i++;

j를 출력하면 몇일까? 4가 나올까?
알 수없다. subexpression의 계산 순서에 따라 6이 될 수도 있다.

  1. 두번째 피연산자인 i++가 먼저계산된다. -> 3 * 2 = 6
  2. 첫번째 피연산자인 i가 먼저계산된다. -> 2 * 2 = 4 이후에 i = 3
    그래서 이 계산식 또한 undefined behavior이다.

5. Expression Statements

C언어는 어떤 표현식이라도 구문(statement)으로 사용될 수 있다.

++i;

++i는 큰 표현식의 일부가 아니고, 구문으로 사용되었기 때문에 결과값은 버려진다. 그리고 다음 구문이 실행된다.

i = 1; //i에 1을 대입함
i--; // i가 감소함
i * j - 1; // i, j의 값이 변하지 않았기 때문에 아무 effect가 없고, 어떠한 의미도 없음

Others


lvalue가 있으면 rvalue도 있지 않을까? rvalue도 존재한다. 대입 연산자의 왼쪽이 lvalue라면 오른쪽이 rvalue이다. rvalue는 변수, 상수, 또는 더 복잡한 표현식이 될 수 있다. C표준에서는 rvalue 대신에 expression이라는 용어를 사용한다.


v가 side effect를 가질 때, v += e와 v = v + e는 같지 않다.
v += e는 딱 한 번만 v가 계산된다. 하지만 v = v + e는 v가 두 번 계산된다. 그러므로 v의 계산에 의한 side effect도 후자의 경우 두 번 발생할 것이다.

a[i++] += 2;

a[i++] = a[i++] + 2;

전자의 구문은 어떠한 문제도 없고 i가 한번 증가할 것이다.
후자의 구문은 undefined behavior이다. 어떠한 동작을 할 지 예측할 수 없기 때문이다.


후위 연산자 ++와 --는 언제 작동할까?
C 표준에서 도입한 개념인 sequence point와 관련이 있는데, 피연산자의 저장된 값을 수정하는 것은 이전 sequence point와 다음 sequence point 사이에서 일어날 것이다.
sequence point는 나중에 다루겠다.

profile
저장용

0개의 댓글