-
[c/c++] call-by-value와 call-by-referenceProgramming/C++ 2019. 1. 8. 19:42
*열혈C 프로그래밍 강의를 듣고 정리한 내용입니다. 잘못된 정보나 오타가 있을시 피드백 부탁드립니다.*
- 들어가기전
함수에게 인자를 전달하는 형태는 여러가지가 있습니다.
C에서는 call-by-value와 call-by-reference가 가장 많이 쓰이는 형태이며
함수의 인자로 전달되는 대상에 따라 함수의 호출 방식이 구분되어집니다.
단순히 값을 인자로 전달하는 경우를 '값에 의한 호출' 즉, call-by-value라 하고
메모리 접근에 사용되는 주소값을 인자로 전달하는 경우를 '참조에 의한 호출' 즉, call-by-reference라 합니다.
Call-by-value (값에 의한 호출) :
변수(메모리 공간에 저장된 값)의 값을 복사하여 함수의 인자로 전달하는 방식
#include <iostream>
using namespace std; void func(int b); int main() { int a = 10; cout << "원본 a의 값은 " << a << endl; func(a); return 0; } void func(int b) { b += 10; cout << "복사된 a의 값은 " << b; system("pause>null"); } - 결과
복사된 변수 a의 값이 func함수에 인자(int b)로 전달된 형태의 call-by-value 예제입니다.
출력된 결과를 통해 알 수 있는 사실은 아래와 같습니다.
1. main 함수(실 매개변수)의 실제값을 복사해서 전달한다.
2. 형식 매개변수의(함수측)값을 변경한다해도 실 매개변수(함수 호출 측)의 값은 변경되지 않는다.
다른 말로, 함수 속 인자의 값이 변경되어도 함수 외부의 값에 적용되지 않는다.
3. 값을 넘겨주는 형태이기 때문에 고비용, 복사손실이 발생한다.
여기서 끝내기엔 2%부족한거 같죠..?
더 쉬운 이해를 돕기위해 swap 예제를 가져왔습니다.
#include <iostream>
using namespace std; void swap(int argA, int argB); int main() { int a = 10; int b = 20; cout << "Before swapping" << endl; cout << "a: " << a << endl; cout << "b: " << b << endl; swap(a,b); cout << "\nAfter swapping" << endl; cout << "a: " << a << endl; cout << "b: " << b << endl; return 0; } void swap(int argA, int argB) { int temp; temp = argA; argA = argB; argB = temp; cout << "\n"; cout << "argA: " << argA << endl; cout << "argB: " << argB << endl; } - 결과
아래는 예제를 요약한 그림입니다.
위 swap 예제를 살펴보면 변수 a와 b의 값이 argA와 argB에 대입되어서 전달됐음을 확인할 수 있습니다.
argA와 argB는 아래와 같은 값 교환을 통해 서로의 값이 뒤바뀐 상태이구요
temp = argA; argA = argB; argB = temp;
출력 결과는 어떨까요? argA와 argB의 값이 바뀌었으니 a와b의 값도 바뀌었을까요? 결론은 아닙니다.
그 이유는 앞에서 이야기했듯이 복사된 값이 변경되어도 원본의 값은 변경되지않기 때문입니다.
위 swap예제는 call-by-value를 사용했지만 '잘못된 방식'으로 사용한 예로, swap프로그램이 원하는
목적에 다다르지 못했습니다.
그렇다면 어떻게 원본의 값을 변경할 수 있을까요?
키(key)는 원본에 직접 접근이 가능한 참조에 의한 호출 즉, call-by-reference 함수 호출 방식입니다.
Call-by-reference (참조에 의한 호출) :
주소값을 함수의 인자로 전달하는 방식
call-by-reference는 바로 swap예제로 call-by-value와 비교해보겠습니다.
// call-by-reference using pointers #include <iostream>
using namespace std; void swap(int* argA, int* argB); int main() { int a = 10; int b = 20; cout << "Before swapping" << endl; cout << "a: " << a << endl; cout << "b: " << b << endl; swap(&a,&b); cout << "\nAfter swapping" << endl; cout << "a: " << a << endl; cout << "b: " << b << endl; return 0; } void swap(int* argA, int* argB) { int temp; temp = *argA; *argA = *argB; *argB = temp; cout << "\n"; cout << "argA: " << *argA << endl; cout << "argB: " << *argB << endl; } - 결과
아래는 예제를 요약한 그림입니다.
call-by-value가 아닌 call-by-reference를 적용한 swap예제입니다.
출력된 결과를 통해 알 수 있는 것은 아래와 같습니다.
1. main 함수(실 매개변수)의 주소값을 swap함수(형식 매개변수)에 전달한다.
2. 형식 매개변수에(함수측)값을 변경하면 실 매개변수(함수 호출 측)의 값도 같이 변경된다.
다른말로, 포인터로 전달받은 인자는 원본의 값을 변경할 수 있다.
3. 고비용, 복사손실 문제를 해결할 수 있다.
포인터를 사용하고있기 때문에 헷갈리시는 분들은 포인터 요약을 참고하세요
swap(&a,&b);
&연산자로 변수 a와 b의 주소를 구해 swap함수의 인자로 전달합니다.
void swap(int* argA, int* argB);
변수 argA와 argB는 포인터로 선언되어 변수의 메모리 주소를 가리킵니다.
int temp; temp = *argA; *argA = *argB; *argB = temp;
swap 함수 내부에서는 *(역참조 연산자)를 사용하고 있습니다. *은 가리키는 공간으로 가는거라했죠
따라서 *argA와 *argB는 포인터가 가리키는 공간에 직접 접근하여 a와 b의 값을 저장합니다.
- 정리하면
swap함수 내에서의 *argA = main함수의 a
swap함수 내에서의 *argB = main함수의 b
argA와 argB가 a와 b의 메모리 주소를 포함하므로
*argA와 *argB에 수행되는 모든 작업은 main()함수의 값을 수정할 수 있다.
Comment