Web/[JS] Common

Call By Value? Call By Reference?

ihl 2021. 9. 4. 23:20

  코딩을 하다보면 Call By Value와 Call By Reference 라는 말을 들어본 적이 있을 것이다. 나는 지금까지 Call By XXX은 함수의 인자로 전달되는 타입에 따라 다르다고 생각했다. Primiteve Type을 넣으면 Call By Value, Reference Type을 넣으면 Call By Reference와 같은 식으로 말이다. 하지만 엄밀히 말하자면 대부분의 프로그래밍 언어는 Call By Value 이다. 왜 그런 것인지 알아보자.

 

1. Call By Value

function addTwo(x) {
  x -= 1;
  console.log(x);
}

let x = 10;
addTwo(10);

console.log(x); // output: 10

  Call By XXX과 같은 말은 함수 호출에서 사용되는 말이다. 위와 같이 함수에 인자 x를 보내면, 함수 내에서 해당 값을 복사하여 별도의 메모리 공간에 저장한 후 사용하므로 함수가 끝난 뒤 x의 값을 확인하면 10이 된다. 원본이 변경되지 않았기 때문이다. 이와 같이 함수 호출에서 인자 값을 복사하여 사용하는 것을 Call By Value라고 한다.

 

2. Call By Reference? 

function addTwo(arr) {
  for(let i = 0; i<arr.length; i++){
    arr[i] = arr[i] + 2;
  }
  console.log(arr); // output: [3, 4, 5]
}

let arr = [1, 2, 3];
addTwo(arr);

console.log(arr); // output: [3, 4, 5]

  Call by Reference란 원래 전달되어야할 실제 파라미터 대신 이를 참조하기 위한 정보가 인자로 들어오는 것이다. JS에서 알다시피 Reference Type을 인자로 주면 Call By Reference의 정의와 유사하게 동작한다. 위 코드에서 전달된 arr 인자(주소)로 실제 값인 arr에 접근하여 원본을 변경하였기 때문이다. 하지만 다음과 같은 예외가 있다.

 

3. Call By Sharing

function fn1(x){
  x = 1; 
}

function fn2(x){
  x.name = 'lll';
}

var obj = {name: 'ihl'}

fn1(obj);
console.log(obj); // { name: 'ihl' }

fn2(obj)
console.log(obj); // { name: 'lll' }

  위 코드를 실행해보면 fn1()에 객체를 인자로 넣은 뒤 다시 객체를 호출했을 때 그 값이 변하지 않은 것을 확인할 수 있다. 왜냐하면 JavaScript는 Call By Value 이므로, fn1() 내의 x는 인자인 객체 obj의 주소를 복사하여 갖고 있다. 따라서 x 자체를 1로 변경해도 메모리의 참조 대상이 객체에서 1로 변경된것 뿐이기 때문에 원본에 영향을 주지 않는 것이다.

 

  즉, JavaScript는 Call By Reference를 지원하지 않고 오직 Call By Value만 지원한다. 이렇게 함수에 객체 인자를 전달했을 때 속성은 변경 가능하지만, 재할당이 불가능한 경우를 Call By Sharing이라는 말로 표현하기도 한다.

 

4. Call By Reference? 다시 보기

function addTwo(arr) {
  for(let i = 0; i<arr.length; i++){
    arr[i] = arr[i] + 2;
  }
  console.log(arr); // output: [3, 4, 5]
}

let arr = [1, 2, 3];
addTwo(arr);

console.log(arr); // output: [3, 4, 5]

  Call By Reference라고 생각했던 코드를 다시 살펴보자. 엄밀하게 생각해보면 인자로 전달된 arr은 주소를 가리키고 있다. 그렇다면 arr의 Value가 주소이기 때문에, 주소를 복사해서 사용한 것으로 이해할 수 있지 않을까? 이렇듯 Call By Reference라고 생각했던 것도 Call By Value로 충분히 설명 가능하다. 

 

#include <stdio.h>

void add(int* x){
    *x = *x + 2;
    printf("argument: %p \n", *x);
}

int main()
{
    int x = 10;
    int *xPtr = &x;
    printf("%d\n", x);

    add(xPtr);
    printf("%d\n", *xPtr);
}

  위 코드는 포인터를 이용하여 원본의 값을 변경한 예시이다. 이렇듯 포인터를 이용하면 Call By Reference를 흉내낼 수 있다. 그러나 C 언어를 Call By Reference 언어라고 할 수는 없다. 위 코드의 add 함수 내에서 *을 사용하지 않으면 원본의 값은 변하지 않기 때문이다. 즉, C 언어도 기본적으로 인자의 원본을 기본적으로 변경할 수 없는 것이 일반적이다. 즉, C언어는 기본적으로 Call By Value이지만, 포인터를 활용하면 Call By Reference를 구현할 수 있는 언어라고 정리할 수 있다.

 


c언어에 Call by reference는 없다: https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=han95173&logNo=220934411280

왜 많은 언어가 Call By Value인가: https://softwareengineering.stackexchange.com/questions/153474/why-are-so-many-languages-passed-by-value/153518

어떤 언어가 Call By Reference인가: https://www.quora.com/Which-programming-languages-are-pass-by-reference

Call by value vs Call by reference: https://perfectacle.github.io/2017/10/30/js-014-call-by-value-vs-call-by-reference/