call-by-reference, call-by-value + call-by-address
주소 | ||
0차 | ||
1차 | ||
&p | ||
p | ||
*p | ||
122EF560 | ||
125F7458 | ||
71 | ||
Note ) java에서 말하는 참조 변수와, C++에서 말하는 참조 변수가 다름을 알아야 혼동을 피할 수 있다. C++에서 참조 변수는 java나 C에는 없는, 변수 자체의 alias 개념이다 ( case 3 참조 ) java에서 참조 변수란 C나 C++에서 말하는 포인터 변수와 동일하다. 즉 배열, 객체 등을 가리키는 변수를 말한다. ( C의 포인터 변수와 다른 점은 참조 변수에 아무 값이나 대입할 수는 없다는 것이다. ) 아마 java가 포인터를 지원하지 않으면서 포인터 변수 보다는 참조 변수라는 말을 쓰는 것 같다.
case 1: call-by-value
java는 항상 call-by-value다 . ( 정확하게는 call-by-sharing = call-by-object 라고 한다. python도 마찬가지. ) 배열이나 객체를 넘길 때 주소가 넘어가니 call-by-reference라고 착각하기 쉽지만
“call-by-value where the value is reference”라고 이해해야 한다.
참조 변수에 저장된 주소(p)가 넘어가게 되는데, 이는 type이 주소( reference )일 뿐 p라는 참조 변수에 저장된 value다. 그리고 받는 측에서 이것을 받아 p와는 다른 새로운 곳에 할당된 p0에 저장한다.
즉 변수에 저장할 “값”을 받았으므로 , call-by-reference가 아니라 call-by-value다.
예제 코드는 차이를 알기 쉽게 모두 CPP pseudo-code로 작성했다.
1
2
3
4
5
6
7
main(){
int \*p;
swap(p);
}
swap(int \*p0){
\*p0 = 0;
}
case 2: call-by-address
그렇다면 포인터에 저장된 주소(a)자체가 변하면 call-by-reference인가? 포인터에 저장된 주소(a)를 변경하려면 포인터 변수 자체의 주소(&a)를 넘겨야 하니 call-by-reference인 것 같지만 변수 자체의 주소를 넘긴다고 해서 항상 call-by-reference인 것은 아니다. C를 사용하다 보면 아래와 같이 포인터를 사용해 외부 함수의 변수에 접근하는 경우가 많은데,
C는 call-by-reference를 지원하지 않는다. 다만 포인터를 사용하면 call-by-reference처럼 쓸 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
main(){
int a, b;
swap(&a, b);
}
/\*call-by-reference, b는 call-by-value라고 착각하기 쉽지만
둘 다 call-by-value다.\*/
swap(int \*a0, int b0){
int tmp;
tmp = \*a0;
\*a0 = b0;
b0 = tmp;
//물론 b는 안바꿔진다만 예를 들기위해.
}
C에서는 이처럼 &a를 파라미터로 넘겨 a를 호출 func에서 수정할 수 있다. &a로 a의 주소를 넘기므로 실제로 main함수의 a값이 변경되지만, 그렇다고 해서 이게 call-by-reference는 아니다. a라는 변수 자체가 넘어간 것이 아니라 case1처럼 주소(&a)를 값으로 취급하고 이 값을 넘긴 것이며, a0이라는 변수에 담을 값을 받은 것이다. ( a0 = &a ) case1과 다른점은 변수 자체의 주소값이 넘어가긴 한다는 점이다.
변수 자체의 주소( &a )를 넘겨 함수에서 a를 수정할 수 있도록 하는 것. 이를 call-by-address 라고 부르는 경우도 있다. 아무튼 swap이라는 함수 내에서 a의 값에 접근할 때는 * 연산자를 사용해서 포인터로 접근하는 수 밖에 없다.
call-by-reference는 다음과 같은 형식이다.
case 3: call-by-reference
1
2
3
4
5
6
7
8
9
10
int main(){
int a, b;
swap(a, b);
}
swap(int &a1, int &b1){
int tmp;
tmp = a1;
a1 = b1;
b1 = tmp;
}
a라는 변수 자체가 넘어가는call-by-reference라면 위와 같이 받는 쪽 에서 argument를 &a로 정의할 수 있어야 한다.
a1이라는 변수에 담을 값을 전달 받은게 아니라, a1자체를 a를 사용하는 것과 마찬가지로 사용할 수 있다.(alias)
C++에서는 이렇게 참조 변수, reference variable을 통해 call-by-reference가 가능하지만, C나 java 등에서는 이를 제공하지 않는다.
reference의 정확한 의미는 다음과 같다.
A reference is an alias, or an alternate name to an existing variable
즉 case3의 a1, b1은 reference variable이며 a, b의 alias다. 그러나 case2의 a0는 type부터가 포인터인, 단순히 a를 가리키는 새로운 pointer variable이다.
call-by-reference라면 *없이 사용 가능해야한다.
참고
https://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_reference