youngfromnowhere
[Java] Call-by-Value/Call-by-Address/Call-by-Reference 본문
함수를 호출할 때 함수에 '무엇을' 전달하느냐에 따라
함수 호출방식을
Call-by-Value, Call-by-Address, Call-by-Reference로 나눈다.
Call-by-Value.
함수에 어떤 변수의 값을 전달한다. 함수는 값을 전달받았을 뿐이므로
함수 외부의 메모리에 영향을 끼치지 못한다.
먼저 A class를 정의한다.
a1.value, a2.value에 1, 2를 저장하고
swap method를 호출할 때는(line 7) 각 객체변수들의 값을 전달하였다.
swap method 내부에서 각 값을 a1, a2라는 로컬변수로 받고
그 값을 swap한다.
그 결과, swap method 내부에서는 a1, a2의 값이 바뀌었지만
swap method 외부의 a1.value, a2.value에는 아무 변화가 없는 것을 볼 수 있다.
Call-by-Address.
그런데, Java에서는 함수를 호출할 때 변수의 주소값을 인자로 전달할 수가 있다.
Java에서 primitive type이 아닌 모든 변수는 reference type이며, reference type의 변수는
data(instance)가 저장된 주소(memory address)를 값으로 갖는다.
따라서 함수 선언과 호출 시에 reference type을 매개변수로 전달받도록 하면
함수 내부에서 외부에 영향을 주도록 할 수 있다.
line 7, swap method를 호출하며 reference type인 a1, a2를 인자로 전달하고 있다.
line 11, swap method는 reference type인 a1, a2를 인자로 받도록 선언되어 있다.
함수 외부에 저장된 a1.value, a2.value의 값이 바뀐 것을 볼 수 있다.
함수 호출시 'reference type'을 전달한다는 점, 함수 외부에 영향을 끼친다는 점 때문에 처음에는 이것이
"Call-by-Reference"에 해당하는 줄 알았다. 하지만 구글링을 통해 얻을 수 있는 답은
"Java는 Call-by-Reference를 지원하지 않는다"였다.
https://stackoverflow.com/questions/40480/is-java-pass-by-reference-or-pass-by-value
Is Java "pass-by-reference" or "pass-by-value"?
I always thought Java uses pass-by-reference. However, I've seen a blog post that claims that Java uses pass-by-value. I don't think I understand the distinction they're making. What is the explana...
stackoverflow.com
The terms "pass-by-value" and "pass-by-reference" have special, precisely defined meanings in computer science. These meanings differ from the intuition many people have when first hearing the terms. Much of the confusion in this discussion seems to come from this fact.
The terms "pass-by-value" and "pass-by-reference" are talking about variables. Pass-by-value means that the value of a variable is passed to a function/method. Pass-by-reference means that a reference to that variable is passed to the function. The latter gives the function a way to change the contents of the variable.
즉, 위의 예시에서 우리는 swap method에 a1, a2의 '주소 값'을 복사하여 전달하였고, swap method에서는 그 주소값을 통해 a1.value, a2.value에 접근하였지만, 어쨌든 전달된 것은 하나의 '정수 값'이므로, call-by-value에 해당한다는 것이다. (call-by 와 pass-by는 실질적으로 같은 의미라고 봐도 좋다.)
이렇게, 함수에 전달되는 것은 하나의 '값'이지만, 그 값의 의미가 '주소'이므로 함수 내에서 외부의 메모리에 접근 가능한 경우를, 원리상으로는 call-by-value지만 그와 의미적으로 구분하기 위해 call-by-address라 부른다.
(위 내용은 돌아보니 너무 뇌피셜이라 취소선 처리함)
그런데, 주소값을 전달하는 것 조차 '값'이 복사되어 전달되는 것이므로 call-by-value라면, call-by-reference는 무엇일까?
Call-by-Reference.
위에 링크된 stackoverflow 질문글에 따르면 "reference"가 함수에 전달되는 것이 call-by-reference이다. 이 때 말하는 reference는 "주소값"이 아니라, C++에서 말하는 reference로, 어떤 변수의 '또 다른 변수명'을 뜻한다. C++에서 변수를 선언할 때 "address operator &"을 이용해 선언하면, 그것은 해당 변수명을 다른 변수의 또 다른 변수명(alias)으로 쓰겠다는 의미가 된다.
(즉, &은 C++에서 이미 존재하는 어떤 변수의 reference를 생성하는 연산자인 셈인데, 이름은 address operator이다. 약간 사람 헷갈리게 하는 부분..)
변수 선언시, 또 함수 선언시에 &은 '주소값을 반환하는 연산자'가 아니라, 'reference'를 만드는 연산자, 즉 alias를 만드는 연산자가 된다!
line 5, a_1에는 a_0의 value가 복사되어 할당된다.
line 6, 반면 b_1은 b_0과 memory address를 공유한다. (b_1을 b_0의 reference로 선언. 즉, b_1은 b_0의 alias이다.)
(cout 으로 각 변수의 주소를 출력. 변수를 호출할 때에는 &는 주소값을 반환하는 연산자가 된다.)
Java의 Call-by-Address(Value)와 C++의 Call-by-Reference의 차이점을 관찰해보기 위해 외부 변수에 접근하여 값을 변조하는 함수를 만들어보자.
line 7, modify method를 호출하면서 a1과 a2를 전달한다. java에서 primitive type이 아닌 것은 reference type이므로, 실제로 전달되는 것은 a1과 a2의 주소값이다.
즉, modify method 안에서 arg1은 a1의 주소값을, arg2는 a2의 주소값을 받게 된다. line 12에서 arg1의 주소값을 통해 arg1.value를 변조한다. 그리고 line 13에서는 arg2에 arg1의 주소값을 할당한다. 따라서 modify method 안에서 arg2.value를 print하면, arg1.value의 변조된 값인 111이 출력된다.
함수 안에서는 arg2에 처음에 전달된, a2의 주소값에 접근한 적이 없다. (arg2에 새 주소값을 할당했을 뿐이다.) 따라서 다시 main method로 돌아와서 a2.value를 출력하면, a2는 변한적이 없으므로 2가 출력된다.
line 24, modify method를 호출하면서 a1, a2를 전달.
line 12, modify method의 선언부에서, arg1, arg2를 전달받은 인자의 reference로 쓰겠다고 선언. 즉, 함수 내부에서 arg1은 a1의 reference 즉 alias가 되고, arg2 는 a2의 alias가 된다.
line 13, arg1.value를 111로 바꾼다.
line 14, arg2 즉, a2에 arg1(a1)의 데이터를 복사하여 저장한다.
modify method 안에서 arg1.value와 arg2.value를 출력하면, java 코드와 같이 111이 똑같이 출력된다. 그런데, C++코드에서는 main method에서도 a2.value가 변화한다. modify method속 arg2가 실질적으로 a2와 같은 객체이기 때문이다. (arg2는 a2의 또 다른 변수명이다.)
결론.
Java에 Call-by-Reference는 없다! 단지 reference type을 전달할 때에는 '주소 값'이 전달되어, 함수 내에서 함수 외부의 데이터에 영향을 줄 수는 있다. 이 것을 Call-by-Address라고 부르기도 한다.
Call-by-Reference는 C++에서, 함수가 인자를 전달 받을때 reference를 지정하여 (즉 또 다른 변수명, alias를 만들어서) 해당 reference가 외부에서 정의된 변수와 완전한 등호관계를 이루는 것을 말한다.
C언어에서 pointer를 이용해 함수 내부에서 외부의 변수를 조작할 때도, call-by-reference가 아니라 call-by-address(value)가 일어난다. 이것을 비교하기 위해 C언어로도 코드를 작성하고 테스트해보았으나 여기에 첨부하면 글이 너무 난잡해질 수 있어서 생략한다.
사실 Call-by-Reference를 지원하지 않는 언어에 대해서도, Call-by-Address에 해당하는 경우를 Call-by-Reference로 소개하는 글도 매우 많은데다가, C언어와 C++ 사이의 *,& operator의 차이점 등등을 알아야 해서 파고들수록 혼동이 가중되는 면이 있었다. 어쨌든 C와 Java에서는 Call-by-Reference를 지원하지 않는다는 점을 기억하고, 실질적으로 함수에 어떤 값이 전달되는가, 함수 내에서 인자(변수명)를 호출할때 실제로 호출되는 대상이 무엇인가에 집중하는 것이 중요하다고 생각한다.
'Java' 카테고리의 다른 글
[Java] Singleton 패턴을 보고 든 의문, 무한 loop가 발생하지 않을까? (0) | 2022.12.08 |
---|---|
[Java] 의도한대로 작동하지 않은 clearScreen() (0) | 2022.11.30 |
[Java] Java compile. 디렉토리 설정 옵션 (0) | 2022.11.25 |
[Java] 수동 컴파일할 때, 소스코드 수정사항이 반영되지 않는 경우 (0) | 2022.11.25 |
[Java] Abstract Class 간단한 메모 (0) | 2022.11.18 |