🖥️ 컴퓨터 싸이언스

C | Call by value와 Call by reference

노바깅 2023. 5. 17. 23:42

 낯선 듯 친숙한 call by value와 call by reference!

 사실 코딩을 배웠다 하면 누구든 한 번은 call by value나 call by reference를 들어봤을 것이다. 요새 하도 파이썬만 봐서 C언어를 거의 까먹는 느낌이라 한 번 정리해보려고 한다! C언어 조교를 같이 하고 있는데 곧 포인터 진도를 나갈 것 같아서 얼른 다시 봐야할 것 같다..ㅎ.. 사실은 이것도 까먹어서 다시 검색해보고 정리하는 중이다...ㅎ

 


 

포인터

 call by value와 call by reference를 정리하기 전에 [yellow]"포인터"[/yellow] 개념을 알아야한다. C언어에서 변수를 선언할 때 보통 [code] int a = 0; [/code] 이런식으로 작성하는데, 포인터 변수를 선언하기 위해서는 [yellow] * [/yellow]를 추가해주면 된다. [code] int *a = NULL; [/code] 이렇게!

 그렇다면 포인터는 뭐에 쓰일까? 정수형 변수가 정수 타입의 데이터를 저장하고 있다면 포인터 변수는 변수의 주솟값을 저장하고 있다. 사실 이렇게만 정리하면 안 와닿을 수도 있는데 아래 코드를 보면 이해가 될 것이다.

#include <stdio.h>

int main(){
    int a = 3;
    int *p = NULL;

    p = &a;

    printf("a의 출력값: %d\n", a);
    printf("p의 출력값: %d\n", p);
    printf("*p의 출력값: %d\n", *p);
    
    return 0;
}

 위 코드의 출력값은 아래와 같다.

// 출력값은 아래와 같다.
a의 출력값: 3
p의 출력값: 1836233372
*p의 출력값: 3

 

 한 줄 씩 살펴보면, 일단 정수형 변수 a에 3을 저장하고, 포인터 변수 p를 비어있는 상태로 선언했다. 그 다음 [yellow][code] p = &a; [/code] 를 이용해서 포인터 변수 p에 a의 주솟값을 저장[/yellow]했다. 이 때, 어? [code]&[/code]가 뭐지! 라고 생각할 수도 있는데 [code]scanf[/code]를 생각해보면 바로 알 수 있다. [code]scanf[/code]는 사용자의 입력을 받아서 변수에 그 값을 저장하는 함수인데, 이 때 변수 앞에 [code]&[/code]를 붙여준다. 일단 여기에서 [code]&[/code]는 변수의 주솟값을 나타내는 역할을 한다고 이해하고 넘어가면 된다!

 그 다음 순서대로 출력을 해보면 p와 *p가 서로 다른 값을 출력하고 있다는 것을 알 수 있다. 위에서 언급했던 것처럼 변수를 선언할 때 *을 붙이면 포인터 변수가 되고, 변수의 주솟값을 저장하게 된다. 그래서 변수 p를 출력하면 우리가 정의한 3이 아닌 다른 값이 출력되는 것이다. 즉 이는 a의 주솟값이라고 보면된다.

 그런데 *p를 출력했더니 a의 값과 동일한 값이 출력됐다. 앞서 포인터 변수는 주솟값을 가지고 있는 변수라고 말했다. *p를 출력한다는 것은, p가 저장하고 있는 주소에 있는 값을 출력한다는 것이다!

 

Call by value

 그럼 이제 call by value에 대해 정리해보자! 일단 가장 대표적인 예시 swap 함수를 생각해보자.

#include <stdio.h>

void swap(int num1, int num2){
    int tmp = num1;
    num1 = num2;
    num2 = tmp;
}

int main(){
    int a = 3;
    int b = 5;
    
    printf("a: %d, b: %d\n", a, b);
    swap(a, b);
    printf("a: %d, b: %d\n", a, b);
    
    return 0;
}

 위의 코드를 실행하면 아래와 같은 출력이 나온다.

a: 3, b: 5
a: 3, b: 5

 

 분명 우리는 swap 함수를 정의했는데, 왜 값이 바뀌지 않는 것일까? 이는 함수 호출을 할 때, 인자 값을 [yellow]복사[/yellow]해서 함수 내에서 사용하기 때문이다. 즉, main 함수에 있는 a와 b가 그대로 swap 함수에서 쓰이는 것이 아니라, 어딘가 다른 공간에 복사되고 복사된 a와 b가 swap 함수에서 사용되기 때문에 main 함수에 있는 원래 a, b는 값이 바뀌지 않는 것이다. 하지만, 함수를 통해서 원래 main 함수에 있는 a와 b의 값을 바꾸고 싶을 때는 어떻게 할까? 바로 그럴 때 포인터가 유용하게 쓰이는 것이다!

 

Call by reference

 앞에서 함수를 호출 할 때 인자를 그대로 사용하는 것이 아니라 어딘가 다른 공간에 복사해서 사용한다고 했다. 그렇다면 직접 그 변수를 사용하고 싶으면 어떻게 해야할까? 아래 코드를 보자!

#include <stdio.h>

void swap(int *num1, int *num2){
    int tmp = *num1;
    *num1 = *num2;
    *num2 = tmp;
}

int main(){
    int a = 3;
    int b = 5;
    
    printf("a: %d, b: %d\n", a, b);
    swap(&a, &b);
    printf("a: %d, b: %d\n", a, b);
    
    return 0;
}

 위의 코드를 실행하면 아래와 같은 출력이 나온다.

a: 3, b: 5
a: 5, b: 3

 값이 바뀌었다! 원하는대로 동작한다!

 그러면 코드를 살펴보자. 우리는 앞서 포인터 변수와 *, &의 의미를 정리했고 swap 함수를 어렵지 않게 이해할 수 있다. 일단, swap 함수는 포인터 변수 num1과 num2를 인자로 받는다. 즉, num1과 num2는 주솟값을 가지고 있는 변수이다. swap 함수 내부에서는 일단 정수형 변수 tmp에 *num1을 대입해주고 있는데, 이는 정수형 변수 tmp에 num1이 갖고 있는 주소에 있는 값을 대입해준다고 해석하면된다! 같은 방식으로 swap 함수를 이해할 수 있다.

 main 함수에서 swap 함수를 호출할 때 call by value를 설명할때와 달리 변수 a와 b에 &를 붙여서 인자를 전달하고 있다. 앞서 &는 변수의 주솟값을 의미한다고 설명했다. swap 함수는 포인터 변수를 입력으로 받는 함수이기 때문에 swap 함수를 호출할 때 주솟값을 전달해주기 위해서 &를 붙인 것이다. 

 

 물론 어떻게 보면 주솟값을 복사해서 전달하는 것이기 때문에 이 역시 call by value 라고 볼 수 있지만 (call by address라고 한다고 한다.) 포인터를 이용해서 call by reference를 구현할 수 있다!