본문 바로가기
CS/Interview

Call by Value 와 Call by reference 차이는 무엇일까요??

by Shark_상어 2023. 1. 26.
728x90

함수 호출 방법은 크게 두 가지가 있다.

 

Call by value(값에 의한 호출)은 인자로 받은 값을 복사하여 처리를 한다.

장점 : 복사하여 처리하기 때문에 안전하다. 원래의 값이 보존이 된다.

단점 : 복사를 하기 때문에 메모리가 사용량이 늘어난다.

 

Call by reference(참조에 의한 호출)은 인자로 받은 값의 주소를 참조하여 직접 값에 영향을 준다.

장점 : 복사하지 않고 직접 참조를 하기에 빠르다.

단점 : 직접 참조를 하기에 원래 값이 영향을 받는다.

 

자바 에서는 기본적인 매개변수는 Call by Value 이지만,

예외적으로 배열 과 클래스는 참조 변수로 Call by reference로 작동한다.

 

https://blog.kakaocdn.net/dn/4aMO7/btrCye8rY71/o8bpjuDRXaM6KxNahkPRW0/img.gif

 

즉 값을 복사해서 처리하냐, 아니면 직접 참조하느냐의 차이이다.

public class SwapTest {
    public static void swap(int a, int b) {
        int tmp = a;
        a = b;
        b = tmp;
    }
 
    public static void main(String[] args) {
        int a = 1;
        int b = 2;
 
        System.out.println(a + " " + b);
        swap(a, b);
        System.out.println(a + " " + b);
        
    }
}

다음과 같이 swap 메소드를 만들어서 기본 자료형 a, b 를 인자로 값을 넘겨서 바꿔보려 했다.

하지만 C언어에서는 포인터를 이용하지만 자바에서도.. 그냥 넘기면 안될꺼 같은데? 라는 생각을 했는데

역시나 값은 바뀌지 않는다.

이유는 a, b 는 Call by value 이기 때문이다. (a, b는 기본 자료형의 변수 이기 때문이다)

 

a,b 를 참조형인 Integer를 이용해서 객체를 생성한 하면 a,b 는 참조 변수가 되므로 Call by reference 가 되겠지? 라고 생각을 해서 되나 안되나 테스트를 해보았다.

public class SwapTest {
    public static void swap(Integer a, Integer b) {
        Integer tmp = a;
        a = b;
        b = tmp;
    }
 
    public static void main(String[] args) {
        Integer a = new Integer(1);
        Integer b = new Integer(2);
 
        System.out.println(a.intValue() + " " + b.intValue());
        swap(a, b);
        System.out.println(a.intValue() + " " + b.intValue());
    }
}

하지만 역시나 바뀌지 않는다.

 

그 이유는 뭘까? 나도 생각하지 못했지만 검색으로 알게된 사실은 Call by reference는 맞지만

메소드 호출을 할 때 새로운 reference 를 만들어 호출하게 만든다는 점이다.

(new 연산자를 이용할 때와 boxing을 이용해서 값을 할당할 때의 차이점에 대해서 좀 더 찾아보면 이해가 갈 것이다)

따라서 swap 메소드에서도 해당 객체를 가르키지만 다른 reference 값으로 가르킨다는 것이다.

그러면 JAVA 에서는 Swap 구현을 어떻게 해야 된다는 말이지?

public class SwapTest {
    int value;
    
    public SwapTest(int value) {
        this.value = value;
    }
    public static void swap(SwapTest a, SwapTest b) {
        int temp = a.value;
        a.value = b.value;
        b.value = temp;
    }
 
    public static void main(String[] args) {
        SwapTest a = new SwapTest(1);
        SwapTest b = new SwapTest(2);
        System.out.println(a.value + " " + b.value);
        swap(a, b);
        System.out.println(a.value + " " + b.value);
    }
}

이 코드에 대해서 설명하자면 아까 위에서 말했던 것처럼

객체를 만들어서 참조 변수인 a, b를 넘기면 Call by reference 는 맞다.

하지만 같은 객체를 가르키는 새로운 reference 값이 생긴다고 했다.

그래서 그 새로운 참조변수 값으로 swap 함수에서 값을 바꿔놓는 것이다.

따라서 가장 중요하게 알아놔야 될 것은

JAVA에서 Call by reference는 해당 객체의 주소값을 직접 넘기는 게 아닌

객체를 보는 또 다른 주소값을 만들어서 넘기다는 사실을 꼭 기억하자.

 

그럼 python은 두 개중에 무엇인가? -> call by assignment 또는 call by object-reference

python의 경우는 위의 경우 처럼 주소 값 참조나, 값 복사와는 다른다. -> python은 모든 것을 객체로 판단하기 때문이다.

파이썬에서 변수를 선언 할 때
a = 'aplace'라고 변수를 선언 하게 되면 'aplace'라는 문자열 객체가 생기고, a라는 이름표를 가지게 된다. python는 위의처럼 메모리에 공간을 할당받은 메모리가 아니라, 어떤 객체에 붙혀지는 이름표 이다.

파이썬에서는 global인지, local인지 영역에 따라 변수들의 정보를 저장하는 namespace가 따로 있다. 즉 전역변수를 함수에서 인자로 받아오더라도 함수 내에서는 지역변수(이름표)에 불가 하다.

예를 들어 전역변수로 list1 = [1, 2, 3, 4]라고 지정을 해놓고 함수 내에서 list1 = [5, 6, 7, 8]로 지정을 해도, 함수가 끝나면 list1 은[1, 2, 3, 4]이다.

그래서 전역 변수를 함수 인자로 받아오더라도 함수 내에서는 지역변수(이름표)에 불가하다.

하지만 immutable 한 포멧의 객체는 변경 할 수 없지만, mutable한 포멧의 객체는 변경 할 수 있다.

728x90