[Java] Call by Value / Reference

박성우·2023년 7월 5일
0

Java

목록 보기
6/6

프로그래밍 언어를 다룰 때 함수 호출 방식에 의해서 크게 Call by Value와 Call by Reference로 나뉜다.

Call by Value

  • 값에 의한 호출
  • 인자로 받은 값을 매개변수로 복사하여 처리

Call by Reference

  • 참조에 의한 호출
  • 인자로 받은 참조를 그대로 사용해서 처리

핵심은 함수를 호출할 때 주어지는 변수, 즉 Caller 함수에서 할당해주는 인자와, 함수가 실행될 때 주어지는 변수, 즉 Callee 함수의 매개변수가 같은지 다른지에 따라 서로 영향을 미치는지 여부로 구분할 수 있다.

원시형(Primitive Type) 변수를 이용한 간단한 예제를 보자.

public class CallByValueExample {
    public static void main(String[] args) {
        int num = 5;
        System.out.println("Before: " + num);
        
        modifyValue(num);
        
        System.out.println("After: " + num);
    }

    public static void modifyValue(int value) {
        value = 10;
    }
}

// Before: 5
// After: 5

main 함수의 num이 modifyValue 함수의 인자로 들어가서 value라는 매개변수에 값이 복사된다.

값이 복사되어서 사용되었기 때문에 함수가 return 되고 main 함수로 돌아와도 num 변수에는 아무 영향이 없다.
-> Call by Value

참조형(Reference Type) 변수를 이용한 예제를 보자.

class Person {
    String name;

    public Person(String name) {
        this.name = name;
    }
}

public class Example {
    public static void main(String[] args) {
        Person person = new Person("Alice");
        System.out.println("Before: " + person.name);

        modifyPersonName(person);

        System.out.println("After: " + person.name);
    }

    public static void modifyPersonName(Person p) {
        p.name = "Bob";
    }
}

// Before: Alice
// After: Bob

Person 객체를 생성하고 해당 Person 객체를 인자로 넘겨줬다. Callee 함수에서는 매개변수로 받은 Person 객체의 필드에 접근해서 변경을 시도하였다. 결국 이번에는 값이 바뀌었다. 그렇다면 Call by Reference 일까?

그렇지 않다. Call by Reference 라고 칭하는 "참조를 넘겨준다"는 뜻과는 엄연히 다른 의미로 Java는 참조형(Reference Type) 변수를 가르키는 주소값을 복사해서 넘겨준다.

좀 이해하기 쉽게 말해보자면, 참조를 넘겨준다는 것은 Caller 함수로부터 인자 자체를 넘겨준다고 볼 수 있고, 주소값을 복사해서 넘겨준다는 것은 해당 변수를 참조만 가능하도록 해준다고 볼 수 있다.

위와 살짝 다른 예제를 보자.

class Person {
    String name;

    public Person(String name) {
        this.name = name;
    }
}

public class Example {
    public static void main(String[] args) {
        Person person = new Person("Alice");
        System.out.println("Before: " + person.name);

        modifyPersonReference(person);

        System.out.println("After: " + person.name);
    }

    public static void modifyPersonReference(Person p) {
        p = new Person("Bob");
    }
}

// Before: Alice
// After: Alice

이번엔 Callee 함수에서 매개변수로 받은 Person 객체의 필드에 접근하는 것이 아니라 매개변수 자체를 새로운 변수값과 함께 새로운 객체로 변경을 시도한다. 하지만 결국 필드값은 바뀌지 않았다.

이 차이가 바로 Call by Value와 Call by Reference의 차이를 보여준다.

객체의 주소값을 복사해서 "Value"로 받아서 사용했기 때문에, 해당 주소값을 참조해서 객체(참조 변수)의 필드를 변경하는 것은 가능했지만, Caller 함수인 main 함수에서 인자로 준 person 변수에는 아무런 영향도 미치지 못한 것이다.

결국, Java에서는 주소값을 복사해주는 것에 그친 Call by Value 만을 지원한다.


그렇다면 포인터를 사용하는 C언어는 Call by Reference일까?

C언어 또한 Call by Value이다.

사실 기존에는 Call by Reference로 알고있었고, 대부분의 교육 과정에서도 포인터를 이용한 Call by Reference라고 가르치는 것을 알고있는데 C언어는 Call by Reference를 지원하지 않는다고 한다.

어떻게보면 C언어의 함수 호출 방식은 Java와 유사하다.

포인터를 넘기는 것 또한 주소값을 넘기는 것과 동일하므로 Java와 같이 넘어온 주소값을 가지고 해당 변수를 참조할 수 있다는 것이다.

그러나, 포인터 또한 어디까지나 주소"값"일 뿐, 참조를 넘기는 것으로는 볼 수 없다는 것이다.

C언어 예제를 보자. (해당 예제는 위 개념을 설명하는데 부족하거나 틀릴 수 있습니다.)

void modifyVar(int *var) {
    *var = 10;
    var = NULL;
}

int main() {
    int num = 5; 
    int *arg = #
    printf("Before: %d\n", num);
    printf("Before: %p\n", arg);
    modifyVar(arg);
    printf("After: %d\n", num);
    printf("After: %p\n", arg);
    return 0;
}

// Before: 5
// Before: 0x7fff98e8a77c
// After: 10
// After: 0x7fff98e8a77c

Caller인 main 함수에서 arg 인자에 num 변수의 주소값을 전달하고 Callee 함수 modifyVar 에서 var를 매개변수로 받는다.

modifyVar 함수 내에서는 var 가 가리키는 변수의 값을 10으로 변경하고 var 자체를 NULL로 변경한다.

var에 의해 변수의 값은 변경되었지만, 포인터 arg는 NULL로 바뀌지 않았다.

이는 Java와 유사하게 C언어 또한 참조 자체를 넘겨주지 못하며, 주소값을 전달할 뿐, 인자와 매개변수는 다른 변수임을 보여준다.

(게다가, C언어의 창시자가 집필한 The C Programming Language 에 의하면 C언어에서 함수의 인자들은 모두 값으로 넘겨진다고 서술되어 있다고 한다.)


그럼 대체 Call by Reference를 지원하는 언어는 무엇인가

메모리 방면에서 보면, C와 같이 포인터를 사용할 경우 호출된 함수의 스택에 넘겨받은 주소를 보관하기 위한 로컬 변수가 생성된다.

하지만 Call by Reference를 지원하는 언어의 경우 스택에 넘겨받은 주소를 보관하지 않는다.

대표적으로 C++의 참조형연산자(&), C#의 "ref" 키워드, FORTRAN 등이 있다.


Reference

https://stackoverflow.com/questions/51054056/is-this-call-by-reference-or-by-value-in-c
https://aomee0880.tistory.com/145
https://bcp0109.tistory.com/360

profile
Backend Developer

0개의 댓글