C++에서는 함수에 인자를 전달하는 방법이 여러 가지 있습니다. 대표적인 방식은 다음과 같습니다.
- 값에 의한 호출(Call by Value)
- 참조에 의한 호출(Call by Reference)
- 주소에 의한 호출(Call by Address)
주소에 의한 호출을 참조에 의한 호출이라고 부르기도 하지만, C++에서 이 둘은 서로 다른 개념입니다. 주소에 의한 호출은 포인터 변수를 사용해 변수의 실제 메모리 주소를 전달하는 반면, 참조에 의한 호출은 참조 변수(원본 변수의 별칭, alias)를 전달합니다. C 언어에는 참조 변수라는 기능이 없기 때문에, 같은 효과를 내려면 포인터를 전달해야 합니다.
이 글에서는 값에 의한 전달과 참조에 의한 전달의 동작 방식과 차이점을 살펴보고, 어떤 상황에서 어떤 방식을 사용하는 것이 좋은지 알아보겠습니다.
값에 의한 호출(Call by Value)
값에 의한 호출 방식에서는 함수 내부에서 인자에 어떤 연산을 수행하더라도 원래 변수의 값은 변경되지 않습니다. 이 방식이 사용되면 해당 변수의 복사본(copy)이 메모리의 스택(stack) 영역에 생성됩니다. 따라서 함수 안에서 값을 변경하더라도 변경되는 것은 복사본일 뿐이며, 원본 값은 그대로 유지됩니다.
예제 코드
#include<iostream>
using namespace std;
void my_function(int x) {
x = 50;
cout << "Value of x from my_function: " << x << endl;
}
int main() {
int x = 10;
my_function(x);
cout << "Value of x from main function: " << x;
}실행 결과
Value of x from my_function: 50 Value of x from main function: 10
함수 내부에서는 x가 50으로 바뀌었지만, main 함수에서 출력한 값은 여전히 10입니다. 이것이 값에 의한 호출의 특징입니다.
참조에 의한 호출(Call by Reference)
참조에 의한 호출 방식에서는 함수 내부에서 수행한 연산의 결과가 실제 원본 변수에 그대로 반영됩니다. 이 방식이 사용되면 해당 변수의 참조(reference) 정보가 스택 영역에 생성되며, 함수는 이 참조를 통해 원본 값에 접근합니다. 따라서 참조를 통해 값을 변경하면 실제 변수의 값도 함께 변경됩니다.
예제 코드
#include<iostream>
using namespace std;
void my_function(int &x) {
x = 50;
cout << "Value of x from my_function: " << x << endl;
}
int main() {
int x = 10;
my_function(x);
cout << "Value of x from main function: " << x;
}실행 결과
Value of x from my_function: 50 Value of x from main function: 50
매개변수 선언에 &(참조 기호)만 추가했을 뿐인데, 이번에는 main 함수에서의 값까지 50으로 변경된 것을 확인할 수 있습니다.
참조에 의한 호출은 언제 사용할까?
참조에 의한 호출은 다음과 같은 경우에 주로 활용됩니다.
- 호출자(caller) 쪽의 변수 값을 변경하고 싶을 때: 함수 내부에서 수정한 결과가 원본 변수에 그대로 반영되기를 원하는 경우 사용합니다.
- 여러 개의 값을 반환해야 할 때: C++ 함수는 기본적으로 하나의 값만 반환할 수 있습니다. 두 개 이상의 결과를 얻어야 한다면, 출력용 인자(output argument)로 참조를 전달하는 방식으로 해결할 수 있습니다.
정리
| 구분 | 값에 의한 호출 | 참조에 의한 호출 |
|---|---|---|
| 전달되는 것 | 값의 복사본 | 원본 변수의 별칭(참조) |
| 원본 값 변경 여부 | 변경되지 않음 | 변경됨 |
| 메모리 비용 | 복사본 생성으로 비용 발생 | 복사 없음, 대용량 데이터에 유리 |
원본 데이터의 보호가 중요하다면 값에 의한 호출을, 원본을 직접 수정하거나 복사 비용을 줄이고 싶다면 참조에 의한 호출을 선택하는 것이 좋습니다. 단, 원본 변경을 막으면서 복사 비용만 줄이고 싶다면 const 참조(const T&)를 사용하는 방법도 고려해 볼 수 있습니다.