인수와 포인터
동작하지 않는 함수
여기서는 다음의 swap()이라는 이름의 함수를 정의해 보도록 한다.
//잘못된 swap 함수의 정의
void swap(int x, int y)
{
int tmp;
tmp = x;
x = y;
y = tmp;
}
swap() 함수는 인수 x와 y를 교환하는 함수로 작성하였다. 이 함수 내에서는 다음과 같은 순서로 변수 x와 y의 값을 교환한다.
① x의 값을 tmp에 대입한다.
② y의 값을 x에 대입한다.
③ tmp의 값(원래의 x)을 y에 대입한다.
변수 tmp를 사용해서 x와 y를 교환하는 것이다. 이것으로 x의 값과 y의 값이 교환될 수 있을 것이다. 그런데 이 함수를 호출해 보면 실제로는 생각과 같이 처리가 수행되지 않는다.
swap() 함수를 실제로 사용해 보도록 한다.
예제) 두 변수의 값 교환(잘못된 예)
인수가 있는 함수 swap()를 사용해서 두 변수의 값을 교환하는 코드를 작성해 보도록 한다. 여기서는 함수의 잘못된 예를 보여준다.
#include <iostream>
using namespace std;
//swap 함수의 선언
void swap(int x, int y);
int main()
{
int num1 = 10;
int num2 = 20;
cout << "변수 num1의 값은 " << num1 << "입니다.\n";
cout << "변수 num2의 값은 " << num2 << "입니다.\n";
cout << "변수 num1과 num2의 값을 교환합니다.\n";
swap(num1, num2); //...①
cout << "변수 num1의 값은 " << num1 << "입니다.\n"; //...②
cout << "변수 num2의 값은 " << num2 << "입니다.\n"; //...②
return 0;
}
//잘못된 swap 함수의 정의
void swap(int x, int y)
{
int tmp;
tmp = x;
x = y;
y = tmp;
}
① 변수 num1과 num2를 교환하기 위해, swap() 함수를 호출하고 num1과 num2를 실인수로 넘겼다.
② 그러나 실행 결과를 보면 변수 num1과 변수 num2의 값은 교환되지 않는다.
값에 의한 전달과 참조에 의한 전달
함수에 실인수를 전달할 때에는 실인수의 '값'만 함수 내로 전달한다. 이와 같은 인수의 전달 방법을 값에 의한 전달(pass by value)이라고 한다. 예를 들면, swap() 함수에서는 변수 num1과 num2의 값인 10과 20만이 함수에 전달되는 것이다.
아래 그림을 살펴보면, 함수 내에 가인수 x와 y를 교환해도 이것은 변수 num1과 num2의 값을 복사한 10과 20을 교환하는데 지나지 않는다. 즉, swap() 함수 내에 값을 교환해도 호출하는 장소의 변수인 num1과 num2에 영향을 줄 수 없다.
그런데 포인터를 잘 사용하면 함수의 호출시에 지정한 인수의 값을 변경할 수 있게 된다. 이 때문에 swap() 함수의 가인수를 포인터로 정의하는 것이 필요하다. 그래서 다음과 같이 swap() 함수를 고쳐보도록 한다.
//swap 함수의 정의
void swap(int *pX, int *pY) //가인수를 포인터로 해 둔다.
{
int tmp;
tmp = *pX; //포인터를 사용해서 값을 교환한다.
*pX = *pY;
*pY = tmp;
}
이번에는 가인수에 포인터를 사용했다. 단, 함수 내에서는 *를 사용해서 포인터가 가리키고 있는 것끼리 교환하기 때문에, 조금 전의 swap() 함수와 내용으로서는 똑같은 것으로 되어 있다. 이 함수를 호출하려면 가인수가 포인터이므로 변수의 주소를 인수로 전달한다. 그러면 이번은 어떻게 될까?
예제) 함수의 인수로 포인터 사용(참조에 의한 전달)
인수가 있는 함수 swap()로 포인터를 사용해서 두 변수의 값을 교환하는 코드를 작성해 보도록 한다.
#include <iostream>
using namespace std;
//swap 함수의 선언
void swap(int* pX, int* pY);
int main()
{
int num1 = 10;
int num2 = 20;
cout << "변수 num1의 값은 " << num1 << "입니다.\n";
cout << "변수 num2의 값은 " << num2 << "입니다.\n";
cout << "변수 num1과 num2의 값을 교환합니다.\n";
swap(&num1, &num2);
//swap() 함수를 호출할 때는 변수 num1과 num2의 주소(&num1, &num2)를 전달한다.
cout << "변수 num1의 값은 " << num1 << "입니다.\n";
cout << "변수 num2의 값은 " << num2 << "입니다.\n";
return 0;
}
//swap 함수의 정의
void swap(int *pX, int *pY)
{
int tmp;
tmp = *pX; //간접 참조 연산자(*)를 사용해서 x와 y의 값을 서로 교환한다.
*pX = *pY;
*pY = tmp;
}
그러면 이번에는 무사히 num1과 num2가 교환되어 있는 것을 알 수 있다.
이와 같이 호출 장소의 변수의 값을 함수 내에서 변경할 경우는 가인수를 포인터로 해 둔다. 그리고 함수는 호출할 때 주소를 함수에 전달하는 것으로 한다. 그러면 가인수는 전달된 주소를 초기화한다. 즉, num1과 num2의 주소가 포인터 pX, pY에 저장된다.
가인수 | 실인수 | |
pX | ← | num1의 주소 |
pY | ← | num2의 주소 |
앞에서 설명한 것처럼 포인터에 *를 붙인 것은 그 포인터가 가리키는 변수와 똑같다는 것을 나타내는 것이다. 즉, *pX와 *pY는 num1, num2와 똑같다는 것을 나타낸다고 생각할 수 있다. 이것을 표로 정리하면
*pX (가인수 측) | ↔ 같음 |
num1 (실인수 측) |
*pY (가인수 측) | ↔ 같음 |
num2 (실인수 측) |
라는 관계가 있는 것이다.
그러므로 이 함수 내에서는
가인수 측의 *pX, *pY를 변경한다 ↔ 실인수 측의 num1, num2를 변경한다.
같다
는 것으로 된다.
아래의 그림을 살펴보면 가인수 측과 실인수 측에는 특정한 관계가 존재한다. 이것으로 함수의 호출에 의해 호출 장소의 변수의 값을 교환할 수 있게 된다. 조금 전의 함수에서는 가인수에 전달되는 것은 실인수 값의 복사본이었고 가인수 측과 실인수 측에는 아무런 관계도 존재하지 않는다. 이와 같이 포인터를 함수의 가인수로 사용하면 실인수 측의 값을 변경할 수 있게 된다.
또한 함수가 호출될 때 실인수의 주소가 함수에 전달되는 것을 참조에 의한 전달(pass by reference)이라고 부른다. 여기서 소개한 포인터를 사용한 함수는 주소를 지정해서 함수에 전달하기 위해 실질적으로 참조에 의한 전달의 함수로 되어 있다.
출처: 박흥복, 서정희. 2015. C++ 프로그래밍 (초보자를 위한). 문운당