본문 바로가기
개발/C, C++

[C/C++] call by value VS call by reference

by 77monkey 개발자 2024. 5. 15.
반응형

 

 

 

 

 

[C/C++] 포인터(Pointer) 개념에 대해서 포스팅을 했었는데, 혹시 기억하시나요? 포인터를 배우면 가장 많이 헷갈리는 부분이 call by reference와 call by value 개념에 대해서 이해를 할 수 있게 됩니다. 코딩을 처음 할 때 가장 헷갈린 부분인데 해당 부분에 대해서 오늘 알아보도록 하겠습니다. 이 부분을 잘 이해해야 원하는 결과를 얻을 수 있습니다. 

 

사전 지식

caller는 함수 호출하는 함수를 의미하고, callee는 호출되는 함수를 의미합니다.

아래 그림과 코드를 잘 보시면서 따라와 주시길 바랍니다. 참고로 편의상 memory를 간략하게 작성했지 실제로 그림과 같이 저장되지는 않습니다.

함수 호출 예시1
함수 호출 예시1

void a_function(int a) {
	printf("a_function: %p\n", &a);
}

int main() {
	int a = 10;

	printf("main: %x\n", &a);
	a_function(a);
	return 0;
}

실제로 코드를 실행시켜 보면, a_function에서의 a 값의 주소와 main에서의 a 값의 주소가 다른 것을 알 수 있습니다. 값 출력도 같이 하게 된다면 10으로 같다는 것을 알 수 있습니다.

main 함수에서 a라는 변수를 선언한 뒤, a의 주소를 출력했습니다. 편의상, main에서 선언한 a를 main_a라고 하겠습니다.

a_function 함수를 호출했습니다. 

a_function 함수는 매개변수 a의 주소를 출력하고 함수가 종료됩니다. 편의상 a_function에서의 a를 func_a라고 하겠습니다. 

자, main_a와 func_a는 같은 값입니다. 실제로 출력을 하면 동일한 10을 출력합니다. 그럼 같은 변수일까요? 안타깝게도 아닙니다. main_a의 값만 복사되어 func_a가 생성이 됩니다. 즉 그림을 보시면, caller, 즉 main에서의 a가 10입니다. 주소는 예시상 0x1000이라고 합시다. 그러면 callee, 즉 a_function에서의 a도 10이 됩니다. 하지만 main_a의 값이 복사되었기 때문에 예시상 0x1004 주소에 10이라는 값을 가지게 됩니다. 

자, 이 개념을 잘 이해하시고 call by value와 call by referecne에 대해서 알아보도록 하겠습니다.

 

call by value

영어를 번역하면 "값에 의한 호출"입니다. 값에 의해서 호출이 되었다는 말이 무엇일까요?

 

동작 방식

함수에 인자를 전달할 때, 해당 인자의 값이 복사되어 전달됩니다. 함수 내에서 전달 받은 매개변수를 변경해도 함수를 부른 곳에서의 변수는 바뀌지 않습니다.

 

특징

  • 함수 내에서 매개변수의 값 변경이 원본 변수에 영향을 주지 않습니다. 
  • 복사된 값이 함수에 전달되므로 함수 내에서 매개변수를 변경하더라도 호출자의 변수 값은 변하지 않습니다. 

예시

a_function 전후로 a를 출력하면 a의 값은 어떻게 될까요?

void a_function(int a) {
	a = 20;
}

int main() {
	int a = 10;

	printf("a: %d\n", a);
	a_function(a);
	printf("a: %d\n", a);
	return 0;
}

네, 맞습니다. a는 a_function 전후로 같은 값을 가리키게 됩니다. 즉, 2번 모두 10을 출력하게 됩니다.

 

call by reference

영어를 번역하면 "참조에 의한 호출"입니다. 호출에 의해서 호출이 되었다는 말이 무엇일까요?

 

동작 방식

함수에 인자를 전달할 때, 해당 인자의 메모리 주소(reference)를 전달합니다. 함수 내에서 매개변수를 변경하면 함수를 부른 곳에서의 변수도 변경됩니다.

 

특징

  • 함수에 변수의 주소(reference)가 전달되므로 함수 내에서 매개변수를 변경하면 호출자의 변수도 변경됩니다. 
  • 포인터나 참조자를 사용하여 구현됩니다.

예시

call by reference 예시는 포인터(Pointer) 개념과 같이 엮이게 됩니다. 그렇기 때문에 조금 헷갈릴 수 있을 것 같습니다. call by value에 비해서 조금 더 자세하게 살펴보겠습니다.

일단 아래 코드에서 a의 값은 a_function 호출 전에는 10, a_ function 호출 후에는 20을 출력합니다. 여기서 조금 혼란이 오실 수 있을 것 같습니다. 하지만 사전 지식에서 그린 그림을 조금 응용하면 바로 이해가 가실 것입니다.

void a_function(int *a) {
	*a = 20;
}

int main() {
	int a = 10;

	printf("a: %d\n", a);
	a_function(&a);
	printf("a: %d\n", a);
	return 0;
}

 

그림을 보시니, 이해가 가시나요? a가 가지고 있던 값은 call by value 처럼 복사가 됩니다. 그런데 포인터 값이 복사가 되는 것은 의미가 없습니다. 왜냐하면 결국에는 우리는 그 값이 가리키는 주소의 값을 접근하기 때문입니다. 즉, main에서의 &a의 주소와 a_function에서의 &a는 다른 값입니다. 여기서 한 발자국 더 가면, 만약에 포인터의 주소를 바꾸고 싶으면 위 코드의 a_function에서 a = 0x1004; 이런 형태로 바꾸면 될까요? 안됩니다. 복사된 값이기 때문에 main에서의 a 변수의 값은 바뀌지 않고 그대로 10을 출력할 것입니다.

함수 호출 예시2
함수 호출 예시2

 

마무리

call by reference와 call by value는 바꾸고자 하는 대상이 무엇인지를 잘 파악하면 좋을 것 같습니다. int ** p;와 같이 선언된 변수를 넘기게 된다면 *p, **p의 값을 변경할 수 있습니다. 왜냐하면 p가 복사된 값이기 때문입니다. 그림을 그려보시면 조금 더 이해하시기 쉬우실 것으로 보입니다.

 

 

 

 

 

 

 

 

반응형

'개발 > C, C++' 카테고리의 다른 글

[C/C++] printf 함수의 모든 것  (0) 2024.05.15
[C/C++] 포인터(Pointer) 개념  (0) 2024.05.13
[C/C++] inline 함수  (0) 2024.05.02
[C/C++] 함수(function)  (0) 2024.05.02
[C/C++] 연산자 우선순위  (0) 2023.09.18