Written by JS970
on
on
Expressions and Assignments
Expressions
- expression에는 표현식과 대입문이 있다.
- 표현식은 값을 표현한다.
- 대입문은 변수의 값을 변경하거나 메모리의 위치를 변경한다.
- 어느 위치를 변경할 것인가 : L-value
- 어떤 값으로 변경할 것인가 : R-value
설계 고려 사항
- 대입 기호가 대칭인가?
- a = a+1(대칭)
- a := a+1(비대칭)
- 대입 기호가 연산자인가, 아니면 그냥 문장을 만드는가?
- a = b = c = 3.14 : c에 3.14 대입, b에 c대입, a에 b대입(C)
- a = b = c = 3.14 : a, b, c에 3.14 대입(python)
연산자
- 초기 프로그래밍 언어의 목적은 산술 계산이었다.
- 산술식은 연산자(
operator
)와 연산항(operand
)으로 구성된다.- 연산자는 연산항 개수에 따라 분류 가능하다.
- 단항 연산자 : 연산항이 하나, ex :
++
- 이항 연산자 : 연산항이 둘, ex :
+
- 삼항 연산자 : 연산항이 셋, ex :
?:
- 단항 연산자 : 연산항이 하나, ex :
- 연산자 위치에 따라 연산자를 분류할 수도 있다. 아래는 단항 연산자의 예시이다.
- 전치(prefix) : 연산자가 연산항 앞에 놓인다, ex :
-
- 후치(postfix) : 연산자가 연산항 뒤에 놓인다, ex :
++
- 전치(prefix) : 연산자가 연산항 앞에 놓인다, ex :
- 연산자는 연산항 개수에 따라 분류 가능하다.
연산자 우선순위
연산자 결합방향
- 전치 단항 연산자 : 우측결합
- 후치 단항 연산자 : 좌측결합
- 지수를 제외한 이항 연산자 : 좌측결합
- 지수 연산자 : 우측결합
- 대입 연산자 : 우측결합
연산자 계산 순서
- 특이하게도
APL
은 연산자의 우선순위가 존재하지 않으며 모든 연산자는 우측결합이다.- 이 때문에 3 * 4 + 5의 값이 27이 나온다.
- 연산 순서에 따른 오버플로우를 생각해야 한다.
- A, C는 큰 양의 정수, B, D는 절대값이 큰 음의 정수라고 하자.
- A + B + C + D 의 값과 A + C + B + D의 값은 오버플로우 발생에 따라 달라진다.
- A, C는 큰 양의 정수, B, D는 절대값이 큰 음의 정수라고 하자.
- 괄호를 사용하여 연산순서를 명확히 할 수 있다(번거롭다)
조건 연산자
-
C
,C++
,Java
,Python
,Lua
등에서 제공되는 삼항 연산자를 의미한다. -
함수형 언어의 if는 대부분 조건 연산자로고 보면 된다.
-
L-value에도 조건 연산자를 쓸 수 있나?
C++
에서는 다음과 같은 표현이 가능하다. (옛날C
에서도 가능)(c ? a : b) = 5;
C
,Java
에서는 불가능하다.
-
연산항 계산 시 side effect발생 여부
-
사용자 정의 연산자에 대한 overloading이 허용되는가?
-
mixed-mode expression이 허용되는가?
값을 구하는 순서
- 연산항의 값을 구하는 순서
- 변수 : 메모리의 값을 가져온다.
- 큰 상수 : 메모리의 값을 가져온다.
- 작은 상수 : 명령어 자체에 기록외어 있다.
- 괄호 표현식 : 괄호 표현식이 연산항으로 사용되기 전에 괄호 내의 모든 연산항과 연산자들이 계산되어야 한다.
- 함수 호출 시 인수를 계산하는 순서 : 정해져 있지 않다.
- 순수한 함수 : 평가 순서가 중요하지 않다.
- 부대효과가 있는 함수 : 평가 순서에 의해 부대효과가 발생할 수 있음에 주의해야 한다.
- 한 수식에 두 번 이상 나타나는 변수에는
++
,--
연산을 사용하지 않는다. - 실제로 이렇게 사용하지 않더라도 메크로 사용 과정에서 이런 상황이 발생할 수 있다.(dirty macro)
- 한 수식에 두 번 이상 나타나는 변수에는
연산자 오버로딩
- 하나의 이름에 여러 기능을 부여한다.
- 일반적으로 사칙연산에 대해 overloading이 많이 일어난다.
- 컴파일러에서 오류 검사가 어렵다는 단점이 있다.
a = &b;
- 위의 경우 이항연산자
&
의 좌항이 생략된 것으로 판단할 수도 있다. - 하지만 이것이 주소연산이나 bitwise연산일 경우 오류가 아니다.
- 마찬가지로 C++에서의 아래와 같은 코드를 생각할 수 있다.
vector<vector<int>> vec;
>>
에서 컴파일러가 오류를 발생시킬 수 있다.
- 위의 경우 이항연산자
- 하지만 코드가 간결해진다는 장점이 있다.
- 연산자 중복지정을 허용하는 언어로는
C++
,Ada
,FORTRAN 90
,Python
등이 있다.
함수의 부수효과(side effect)
- 함수에서 의도한 결과 또는 출력 이외의 다르게 변경되는 효과를 의미한다. 부대효과라고도 한다.
- 함수가 부대효과를 발생시킨다면, 연산자의 계산 순서에 따라 값이 달라질 수 있다.
a = 10; b = a + fun(&a);
- 위 코드에서 a에 fun(&a)를 더할지, fun(&a)를 연산한 값에 a를 더할 지에 따라 b의 값이 변한다.
- 연산자 역시 같은 문제가 있다.(증감연산자)
- 프로그램 수행을 예측하기 힘들게 한다.
- 입출력 인수, 비지역 변수, I/O operation등의 원인으로 부수효과가 발생한다.
해결 방법
- 함수의 부수효과를 발생시킬 수 없도록 한다.
- 입출력 인수를 허용하지 않는다.
- 비지역 변수 참조를 허용하지 않는다.
- 하지만 이 경우 입출력 인수와 비지역 변수 참조가 필요한 경우에 대해 처리할 수 없다.
- 모든 연산자의 피연산자 계산순서를 고정시킨다(Java)
- 장점 : 연산 계산 순서를 정확히 예측할 수 있다.
- 단점 : 어떤 컴파일러 최적화는 수행될 수 없다.
- 경우에 따라 함수의 부수효과를 검사한다.
- 함수 호출이 어떤 식에 포함될 경우에는, 해당 함수가 부수효과를 일으키지 않는 경우에만 맞는 것으로 간주한다.(FORTRAN 77)
- 함수가 부수효과를 발생시키지 않는지 검사하는 것 자체가 힘들기 때문에 어렵다.
int a = 7; int zero() { return a - a; }
- 이 경우에도 함수가 부수효과를 일으킨다고 판단할 수 있다.
- False positive가 많아 현실적인 문제가 있다.
형 변환
- 아래는 형 변환의 유형이다.
- 축소변환(narrowing conversion) : 원래 타입의 모든 갑을 포함하지 못하는 형으로의 변환
- 확장변환(widening conversion) : 원래 타입의 모든 값을 포함하는 형으로의 형으로의 변환
- 묵시적 타입변환(implicit type conversion) :
type coercion
- 타입 오류 검출 능력이 저하된다.
- 대부분의 언어에서 수치 자료의 확장변환에 대해서만 제한적으로 허용한다.
- 명시적 타입변환(explicit type conversion) :
type casting
- 확장변환, 축소변환 모두 허용한다.
- 축소변환의 경우 일반적으로 경고 메시지를 발생시킨다.
- 혼합 자료형 표현식(mixed-mode expression)
- 연산항의 형이 다른 표현식이다.
- 연산자가 가정하는 형이 결정되어 있으므로, 계산하기 위해서는 어떤 방향으로든 형 변환이 필요하다.