Post

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
8
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
14
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
11
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

http://stackoverflow.com/questions/40480/is-java-pass-by-reference-or-pass-by-value/12429953#12429953

http://stackoverflow.com/questions/33622787/are-pointers-considered-a-method-of-calling-by-reference-in-c

This post is licensed under CC BY 4.0 by the author.