youngfromnowhere

[CS] Pass by reference 언어별 비교 본문

CS&Concepts

[CS] Pass by reference 언어별 비교

곽일땡 2023. 7. 3. 14:50

https://youngnowhere.tistory.com/43

 

[Java] Call-by-Value/Call-by-Address/Call-by-Reference

함수를 호출할 때 함수에 '무엇을' 전달하느냐에 따라 함수 호출방식을 Call-by-Value, Call-by-Address, Call-by-Reference로 나눈다. Call-by-Value. 함수에 어떤 변수의 값을 전달한다. 함수는 값을 전달받았을

youngnowhere.tistory.com

 

위 글에서 call by value와 call by reference의 차이에 대해 고찰한 바 있다. java에서 함수에 reference type의 변수가 전달되는 case와 c++의 사례를 비교하면서 어째서 java에는 'call by reference'가 지원되지 않는다고 하는지에 대해 얘기하였다.

 

(call by ... 와 pass by ...는 엄밀히 의미는 다르지만 사실상 같은 얘기이다. 왜냐면 어떤 함수에 data를 넘겨준다-pass-는 것은 그 함수를 호출-call-해야 가능하기 때문이다.)

 

https://youngnowhere.tistory.com/49

 

[PHP] php & operator : reference 연산자

PHP의 & 연산자는 C언어의 포인터와 자주 비교되는데, 정확한 의미는 한 변수에 또 다른 변수명(alias)을 붙여주는 것이다. 따라서 PHP의 reference는 실제 memory address를 가리키는 C언어의 포인터보다는

youngnowhere.tistory.com

한 편 위 링크에서는 php의 reference 연산자에 대해 살펴봤다.

 

이번 글에서는 위에서 살펴본 java와 c++의 사례를 다시 정리하고, php에서는 pass by reference가 c++과 같은 방식으로 일어나는지 알아보고 내친김에 python에서 함수에 data를 넘겨줄 때는 어떤 현상이 일어나는지 살펴보자.

 


Java

 

A.java

class A {
    public int value;
    A(int i) {
        this.value = i;
    }
}

passbyref.java

public class passbyref {
    public static void modify(A arg1, A arg2) {
        arg1.value = 111;
        arg2 = arg1;
        System.out.println("moify method, arg1.value : " + arg1.value);
        System.out.println("moify method, arg2.value : " + arg2.value);
    }
    public static void main(String[] args) {
        A a1 = new A(1);
        A a2 = new A(2);
        System.out.println("main method, initial a1.value : " + a1.value);
        System.out.println("main method, initial a2.value : " + a2.value);
        modify(a1, a2);
        System.out.println("main method, final a1.value : " + a1.value);
        System.out.println("main method, final a2.value : " + a2.value);

    }
}
main method, initial a1.value : 1
main method, initial a2.value : 2
moify method, arg1.value : 111
moify method, arg2.value : 111
main method, final a1.value : 111
main method, final a2.value : 2

modify method를 호출하면서 arg1에 a1의 object의 주소값이, arg2에 a2의 주소값이 전달된다.

modify method 내부에서, arg1을 통해 a1의 주소에 접근하여 a1.value의 값을 111로 바꾼다.

따라서 modify method 외부 즉, main method에서도 a1.value의 값은 111로 나타난다.

 

arg2 = arg1;에서 arg2에 arg1의 값 즉, a1의 주소값을 할당한다.

arg2는 arg1과 함께 a1을 가리키므로 modify method 내부에서 arg1.value와 arg2.value는 같다.

a2의 주소에 접근하여 인스턴스 변수의 값을 바꾼 적은 없으므로 main method에서 a2.value의 값은 초기값 2가 그대로 유지된다.

 

파일명을 passbyref.java라 하였지만, 이 자바 코드에서는 단지 함수에 전달되는 변수가 java의 reference type인 것이지 본래 말하는 pass-by-reference가 적용된 것이 아니다.


C++

 

passbyref.cpp

#include <iostream>
using namespace std;

class A {
    public:
        int value;
        A(int value) {
            this->value = value;
        }
};

void modify(A& arg1, A& arg2) {
    arg1.value = 111;
    arg2 = arg1;
    cout << "modify method, arg1.value : " << arg1.value << endl;
    cout << "modify method, arg2.value : " << arg2.value << endl;
};

int main() {
    A a1 = A(1);
    A a2 = A(2);
    cout << "main method, initial a1.value : " << a1.value << endl;
    cout << "main method, initial a2.value : " << a2.value << endl;
    modify(a1, a2);
    cout << "main method, final a1.value : " << a1.value << endl;
    cout << "main method, final a2.value : " << a2.value << endl;
    return 0;
};
main method, initial a1.value : 1
main method, initial a2.value : 2
modify method, arg1.value : 111
modify method, arg2.value : 111
main method, final a1.value : 111
main method, final a2.value : 111

modify 함수의 선언부에서, arg1을 첫번째 전달받는 인자의 reference(alias, 즉 또다른 변수명)로, arg2를 두번째 전달받는 인자의 reference로 쓰겠다고 선언했다. 이제 modify(a1, a2);로 함수를 호출할 때 일어나는 것이 pass-by-reference이다.

 

java 코드에서 modify 함수 내부의 arg1, arg2는 reference type으로서 "어떤 object의 주소값을 담는 변수"로서 작용하고 그 뿐이다. 반면 c++코드에서 modify 함수 내부의 arg1, arg2는 a1, a2를 전달받으면서 a1, a2의 변수명으로 지정되며 따라서 a1, a2 그 자체가 된다.

 

그래서 modify 내부의 arg2 = arg1; 행의 의미가 달라진다. java 코드에서는 arg1에 담겨있는 a1의 주소값을 arg2라는 로컬 변수에 복사해서 할당한다는 의미였다. (arg2에 담기는 주소값이 달라진다.)

c++에서는 arg1 (즉, a1)의 data를 복사하여 arg2 (a2)에 담게 된다. 즉, arg2와 a2의 assignment가 끊어지지 않는다. 따라서 modify 함수 외부에서 a2.value의 값이 달라져있는 것을 볼 수 있다.

 


PHP

 

php에서도 reference operator를 사용하여 변수 선언 혹은 함수 선언시에 어떤 변수명을 다른 변수의 reference로 지정할 수 있다. 그럼 php에서도 pass-by-reference가 지원되는지 알아보자.

<?php
class A {
    public $value;
    public function __construct($value) {
        $this->value = $value;
    }
}

function modify(&$arg1, &$arg2) {
    $arg1->value = 111;
    $arg2 = $arg1;
    echo "modify method, \$arg1->value : $arg1->value\n";
    echo "modify method, \$arg2->value : $arg2->value\n";
}

$a1 = new A(1);
$a2 = new A(2);
echo "main method, initial \$a1->value : $a1->value\n";
echo "main method, initial \$a2->value : $a2->value\n";
modify($a1, $a2);
echo "main method, final \$a1->value : $a1->value\n";
echo "main method, final \$a2->value : $a2->value\n";

?>
main method, initial $a1->value : 1
main method, initial $a2->value : 2
modify method, $arg1->value : 111
modify method, $arg2->value : 111
main method, final $a1->value : 111
main method, final $a2->value : 111

modify 함수 선언시에 reference operator &를 이용하여 $arg1, $arg2를 전달되는 인자의 reference로 쓰겠다고 명시하였다. 결과는 cpp의 경우와 똑같다.

즉, modify($a1, $a2); 로 함수를 호출한 순간 $a1과 $arg1, $a2와 $arg2는 완전히 같은 변수가 된다. 그리고 내부에서 $arg2 = $arg1;이 실행될 때, $arg1 ($a1)의 data가 복사되어 $arg2 ($a2)에 저장된다. 따라서 modify 함수 외부에서도 $a2->value의 값은 111로 변화되어 있다.

 

위의 코드에서 reference operator &를 없애면 java 코드와 똑같이 동작한다. 다시말해 java 코드에서 pass-by-reference는 일어난 적이 없음을 보여주는 것이다.

 


Python

 

#!/usr/bin/python3
class A:
    def __init__(self, value):
        self.value = value

def modify(arg1, arg2):
    arg1.value = 111
    arg2 = arg1
    print("modify method, arg1.value : ", arg1.value)
    print("modify method, arg2.value : ", arg2.value)

if __name__ == '__main__' :
    a1 = A(1)
    a2 = A(2)
    print("main method, initial a1.value : ", a1.value)
    print("main method, initial a2.value : ", a2.value)
    modify(a1, a2)
    print("main method, final a1.value : ", a1.value)
    print("main method, final a2.value : ", a2.value)
main method, initial a1.value :  1
main method, initial a2.value :  2
modify method, arg1.value :  111
modify method, arg2.value :  111
main method, final a1.value :  111
main method, final a2.value :  2

Python의 경우는 Java 코드와 같은 결과를 보인다.

<Data Structures & Algorithms in Python> (Goodrich) 에서는 함수를 호출할 때 parameter와 argument가 서로 alias로서 assign된다고 설명하고 있으나, "reassignment simply breaks the alias."라고 설명한다. 즉, arg2 = arg1에서, arg2라는 parameter에 arg1이 assign되는 순간 함수 호출 시에 형성된 arg2와 a2 사이의 alias 관계는 해제된다는 것이다.

결과적으로 modify method 내부에서 arg1.value를 통해 a1.value의 값을 바꿀 수 있었으나 a2.value의 값은 변하지 않았다.

'CS&Concepts' 카테고리의 다른 글

[CS] Sync. and Async.  (1) 2022.11.22