Call By Value
- 함수가 호출될 때, 메모리 공간 안에서는 함수를 위한 별도의 임시 공간이 생성된다. (c++의 경우 stack frame) 함수가 종료되면 해당 공간은 사라진다.
- 스택 프레임(Stack Frame) : 함수 호출시 할당되는 메모리 블록(지역변수의 선언으로 인해 할당되는 메모리 블록)
- call-by-value 값에 의한 호출방식은 함수 호출시 전달되는 변수의 값을 복사하여 함수의 인자로 전달한다.
- 복사된 인자는 함수 안에서 지역적으로 사용되는 local value의 특성을 가진다.
- 따라서 함수 안에서 인자의 값이 변경되어도, 외부의 변수의 값은 변경되지 않는다.
- Java의 경우 함수에 전달되는 인자의 데이터 타입에 따라서 (원시자료형 / 참조자료형) 함수 호출 방식이 달라진다.
- 원시 자료형 (primitive type) : call-by-value 로 동작 (int, short, long, float, double, char, boolean )
- 참조 자료형 (reference type): call-by-reference 로 동작 (Array, Class Instance)
Call By Reference
- 함수가 호출될 때, 메모리 공간 안에서는 함수를 위한 별도의 임시 공간이 생성된다. (예: stack frame) 함수가 종료되면 해당 공간은 사라진다.
- call-by-reference 참조에 의한 호출방식은 함수 호출시 인자로 전달되는 변수의 레퍼런스를 전달한다. (해당 변수를 가르킨다.)
- 따라서 함수 안에서 인자의 값이 변경되면, 아규먼트로 전달된 객체의 값도 함께 변경된다.
JAVA는 기본적으로 모든 전달 방식이 Call By Value이다.
https://loosie.tistory.com/486
In Call By Reference, the address (name) of the actual parameter at the time of procedure call is passed to the procedure as the value to be associated with the corresponding formal parameter. References to the formal parameter in the procedure body result in indirect addressing references through the formal parameter values are immediately transmitted to the ceiling procedure, because both the acutal parameter and the formal parameter refer to the same register.
Call By Reference에서 프로시저 호출 시 실제 파라미터의 주소(이름)가 해당 형식 파라미터와 연관될 값으로 프로시저에 전달됩니다. 보조 파라미터(값을 전달받는)와 공식 파라미터(값을 제공하는)가 모두 동일한 레지스터를 참조하기 때문에 절차 본문에서 형식 파라미터 값을 통한 간접 주소 지정 참조는 즉시 맨 위의 절차로 전송됩니다.
"Semantic Models of Parameter Passing" by Richard E Fairley, March 1973.
이 개념을 보고 판단해보면 Reference는 두 파라미터가 모두 같은 주소값을 가리키고 있는 것이지 보조 파라미터가 해당 주소값을 '값'으로 들고 있는 것이 아니라고 정의를 내릴 수 있다.
1. 새로운 객체 참조
Java에서는 참조 타입을 사용하면 각 타입의 변수들은 주소값을 가지게 된다. 이는 주소 '값'을 가지게 되는 것이므로 Call By Value로 동작한다.
public class CallByEx {
public static void main(String[] args) {
String s = new String("abc");
String s2 = s;
foo(s);
System.out.println("#3 " + s.equals("abc")); // true
System.out.println("#4 " + s.equals("ccc")); // false
System.out.println("#5 " + (s==s2)); // true
//만약 call by reference라면
// System.out.println(s.equals("ccc")); // true
// System.out.println(s==s2); // true
}
static void foo(String str) {
System.out.println("#1 " +str.equals("abc")); // true
str = new String("ccc");
System.out.println("#2 " + str.equals("ccc")); // true
}
}
자바가 Call By Reference라면 해당 String 변수 s는 foo(String str)에서 새롭게 생성된 new String("ccc")를 참조해야 하지만 결과를 보면 그렇지 않다.
2. 참조 타입 객체 값 변경하기
참조 객체가 각각 독립적인 저장공간에 주소값을 가지고 있는 것을 알게되었다.
하지만 Java또한 참조 객체를 다른 객체에게 주소값을 전달하여 대신해서 값을 변경할 수 있는 구조를 가지고 있다.
이러한 동작때문에 Call By Reference로 착각할 수 있지만 이는 같은 주소값을 가지고있는 보조 객체가 주소값을 통해 접근하여 주소값이 가리키는 내용을 변경시키는 것이기 때문에 Call By Value이다.
public class CallByEx {
public static void main(String[] args) {
int[] src = {1,2,3};
foo(src);
System.out.println(Arrays.toString(src));
boo(src);
System.out.println(Arrays.toString(src));
}
static void foo(int[] arr) {
arr = new int[]{3,4,5};
arr[0] = 9;
}
static void boo(int[] arr) {
arr[0] = 2; // 이건 주소 값의 가리키는 변수를 바꾼 것
}
}
foo(int[] arr);
위에 String객체로 했던 예시와 같이 새로운 배열을 생성하여 값을 변경하면 원본 객체에게는 전혀 영향이 가지 않는다.
boo(int[] arr);
위의 코드를 그림으로 보면 다음과 같다. 자바 참조타입 객체는 주소값을 가지고 있기 때문에 보조 객체(arr)가 주소값을 통해 주소값이 가리키고 있는 값을 변경하면 같은 주소값을 가리키고 있는 원본 객체(src)의 값도 변경이 되는 것이다.
참고
https://wayhome25.github.io/cs/2017/04/11/cs-13/
https://loosie.tistory.com/486