[C, C++] Call by value, Call by reference 쉽게 이해하기

인트로

(본 포스팅은 포인터와 관련이 깊습니다.)

 

함수의 호출 방법은 대표적으로 Call by value(값에 의한 호출)Call by reference(참조에 의한 호출)가 있다.

 

함수 호출이란 말 그대로 정의된 함수를 호출하는 것으로 함수에 정의한 매개변수의 형태에 따라 Call by value 혹은 Call by reference인지 결정된다.

 

참고로 이 둘의 차이를 쉽게 이해하려면 포인터와 Stack을 어느 정도 알고 있어야 한다.

Call by value

Call by value란 함수 호출 시 넘기는 인자의 이 매개변수에 복사(Copy)돼서 함수 내에서 매개변수에 직접적인 데이터 조작을 가해도 인자에 전혀 영향을 주지 않는 것이다. 여기서 이란 주소 값이 아닌 그 자체로 유의미한 데이터라고 생각하면 이해하기 편하다.

 

너무나 뻔한 예제인데 여기 두 변수의 값을 바꾸는 Swap 함수가 있다. 

a와 b는 각각 10과 20으로 초기화했고 함수는 인자로 받은 두 값을 바꾸는 작업을 수행한다.

#include <iostream>

using namespace std;

void Swap(int x, int y)
{
	int tmp = x;
	x = y;
	y = tmp;
}

int main()
{
	int a = 10;
	int b = 20;

	Swap(a, b);

	cout << "a : " << a << endl;
	cout << "b : " << b << endl;
}

 

하지만 기대와 다르게 두 값은 바뀌지 않았다.

 

이유는 간단한데 지역변수와 매개변수는 그 값이 Stack에 할당된다.

예제 코드 main의 지역변수 a와 b 그리고 Swap의 x, y가 그러하다.

Swap함수를 호출하는 순간 매개변수 x, y가 Stack에 할당되고 a와 b의 값이 각각 x, y에 복사된다.

따라서 Swap함수에서 값의 변화를 신나게 줘도 인자에 어떤 영향도 가지 않으며 우리는 이것을 Call by value라고 부른다.

 

Call by reference

엄밀히 말하면 Call by reference도 인자의 값이 매개변수에 복사(Copy)된다는 점은 동일하다. 다만 복사되는 값이 데이터의 주소 값이라는 차이점이 존재한다. 쉽게 말하면 Call by reference란 말 그대로 참조값으로 함수를 호출했단 뜻이다. 여기서 참조라는 개념이 우리가 잘 알고 있는 포인터이다.

아래 코드에서 포인터를 사용한 Call by reference 함수 호출을 확인할 수 있다.

#include <iostream>

using namespace std;

void Swap(int *a, int *b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}

int main()
{
	int a = 10;
	int b = 20;

	Swap(&a, &b);

	cout << "a : " << a << endl;
	cout << "b : " << b << endl;
}

 

이젠 잘 바뀐 걸 확인할 수 있다.

중요한 차이점은 매개변수가 포인터 변수로 정의되었단 점이다. 변수의 주소를 인자로 받으니 Swap함수 내에선 *역참조 연산자로 주소에 저장된 값에 직접 접근할 수 있게 돼서 이전과 다르게 a와 b의 값을 바꿀 수 있게 된 것이다.