프로그래밍 언어/[ Java ]

[ Java ] 05. Call by value vs Call by reference

kim.svadoz 2021. 6. 13. 13:42
728x90
반응형

Call by value vs Call by reference


call by value

값에 의한 호출

함수가 호출될 때, 메모리 공간 안에서는 함수를 위한 별도의 임시공간이 생성됩니다.(종료 해당 공간 사라짐)

call by value 호출 방식은 함수 호출 시 전달되는 변수 값을 복사해서 함수 인자로 전달합니다.

이 때 복사된 인자는 함수 안에서 지역적으로 사용되기 때문에 local value 속성을 가집니다.

따라서, 함수 안에서 인자 값이 변경되더라도, 외부 변수 값은 변경되지 않습니다.

예시

void func(int n) {
    n = 20;
}
void main() {
    int n = 10;
    func(n);
    printf("%d", n);
}
10

call by reference

참조에 의한 호출

call by reference 호출 방식은 함수 호출 시 인자로 전달되는 변수의 레퍼런스를 전달합니다.

따라서, 함수 안에서 인자 값이 변경되면 Argument로 전달된 객체의 값도 변경됩니다.

void func(int *n) {
    *n = 20;
}
void main() {
    int n = 10;
    func(&n);
    printf("%d", n);
}
20

Java의 함수 호출 방식

자바는 항상 call by value로 값을 넘깁니다.

c/c++와 같이 변수의 주소값 자체를 가져올 방법이 없으며, 이를 넘길 수 있는 방법 또한 없습니다.

reference type(참조 자료형)을 넘길 시에는 해당 객체의 주소값을 복사하여 이를 가지고 사용합니다.

따라서, 원본 객체의 Property까지는 접근이 가능하나, 원본 객체 자체를 변경할 수는 없습니다.

User a = new User("sunghyun"); // 1

foo(a);

public void foo(user b) {        // 2
    b = new User("kimchi");        // 3
}
  • 1 : a에 User 객체 생성 및 할당 (새로 생성된 객체의 주소값을 가지고 있다.)

    a ---------------> User Object [name = "sunghyun"]

  • 2: b라는 파라미터에 a가 가진 주소값을 복사하여 가짐

    b ---------------> User Object [name = "sunghyuun"]

  • 3 : 새로운 객체를 생성하고 새로 생성된 주소값을 b가 가지며 a는 그대로 원본 객체를 가리킨다.

    a ---------------> User Object [name = "sunghyuun"]

    b ---------------> User Object [name = "kimchi"]

파라미터에 객체/값의 주소값을 복사하여 넘겨주는 방식을 사용하고 있는 Java는 주소값을 넘겨 주소값에 저장되어 있는 값을 사용하는 call by reference라고 오해할 수 있는데요.

c/c++에서는 생성한 변수마다 새로운 메모리 공간을 할당하고 이에 값을 덮어 씌우는 형식으로 할당합니다. (* 포인터를 사용한다면, 같은 주소값을 가리킬 수 있도록 할 수 있다.)

Java에서 또한 생성한 변수마다 새로운 메모리 공간을 갖는 것은 마찬가지지만, 그 메모리 공간에 값 자체를 저장하는 것이 아니라 값을 다른 메모리 공간에 할당하고 이 주소값을 저장하는 것입니다.

이를 다음과 같이 나타낼 수 있습니다.

  C/C++        |        Java
               |
a -> [ 10 ]    |   a -> [ XXXX ]     [ 10 ] -> XXXX(위치)
b -> [ 10 ]    |   b -> [ XXXX ]
               |
             값 변경
a -> [ 11 ]    |   a -> [ YYYY ]     [ 10 ] -> XXXX(위치)
b -> [ 10 ]    |   b -> [ XXXX ]     [ 11 ] -> YYYY(위치)

b = a;일 때 a값을 b의 값으로 덮어 씌우는 것은 같지만, 실제 값을 저장하는 것과 값의 주소값을 저장하는 것의 차이가 존재합니다.

즉, Java에서의 변수는 [할당된 값의 위치][값]으로 가지고 있는 것입니다.

c/c++에서는 주소값 자체를 인자로 넘겼을 때 값을 변경하면 새로운 값으로 덮어 쓰여 기존 값이 변경되고
Java에서는 주소값이 덮어 쓰여지므로 원본 값은 전혀 영향이 가지 않는 것입니다.(객체의 속성값에 접근하여 변경하는 것은 직접 접근하여 변경하는 것이므로 이를 가리키는 변수들에서 변경이 일어난다.)

객체 접근하여 속성값 변경

a : [ XXXX ]  [ Object [prop : ~ ] ] -> XXXX(위치)
b : [ XXXX ]

prop : ~ (이 또한 변수이므로 어딘가에 ~가 저장되어있고 prop는 이의 주소값을 가지고 있는 셈)
prop : [ YYYY ]    [ ~ ] -> YYYY(위치)

a.prop = * (a를 통해 prop를 변경) 

prop : [ ZZZZ ]    [ ~ ] -> YYYY(위치)
                   [ * ] -> ZZZZ

b -> Object에 접근 -> prop 접근 -> ZZZZ

위와 같은 이유로 Java에서 인자로 넘길 때는 주소값이란 값을 복사하여 넘기는 것이므로 call by value라 할 수 있다.

( 출처 : Is Java “pass-by-reference” or “pass-by-value”? - Stack Overflow )

정리

Call by value의 경우 데이터 값을 복사해서 함수로 전달하기 대문에 원본의 데이터가 변경될 가능성이 없다. 하지만 인자를 넘겨줄 때마다 메모리 공간을 할당해야 해서 메모리 공간을 더 잡아 먹습니다.

Call by reference의 경우 메모리 공간 할당 문제는 해결했지만, 원본 값이 변경될 수 있다는 위험이 존재합니다.

출처
https://github.com/gyoogle/tech-interview-for-developer

728x90
반응형