본문 바로가기
Software/C++&MFC 핵심노트

[C++/MFC 핵심노트] Function(함수) - call by value & call by reference

by lovey25 2019. 11. 6.
반응형

2021-04-15 update log: 기존에 사용한 예제가 C언어 스타일이라서, C++ 스타일의 예제 코드를 추가하였습니다.


함수에 Argument(아규먼트)를 전달하는 방법에는 2가지 가 있습니다. 값을 전달하는 방법과 참조를 전달하는 방법이 있는데요. 많은 곳에서 call by value, call by reference라는 이름으로 한 번쯤 보신 적이 있을 겁니다.

call by value와 call by reference 차이점

call by value라는 건 Argument(인자)의 값을 그대로 함수의 Parameter(매개변수)에 복사를 해서 사용하는 방법이고, call by referece는 Argument의 참조만 복사를 해서 함수의 Parameter로 사용하는 것을 말합니다.

저의 이해를 바탕으로 그림을 그려봤습니다.

call by value로 호출할 때 전달되는 Argument는 원본인 데이터 "A"는 복사가 되어 새로운 "A"가 함수로 들어가게 됩니다. 그래서 함수 안에서 Parameter에 어떤 수정을, 여기서는 Parameter를 "B"로 수정을 했는데요, 한 Parameter는 복사된 새로운 데이터기 때문에 원본에는 아무런 영향을 미치지 못했습니다.

반면, call by reference로 호출하게 되면 함수에 어떤 값이 복사되어 들어가는 건 동일하지만 데이터 값이 아니라 원본 데이터인 "A"가 저장된 주소 값(#1)이 복사가 되어 전달이 된다는 점이 차이점입니다. 함수 내부에서 Parameter를 "B"로 수정하는 동작을 수행할 때 전달된 주소 값으로 데이터에 접근을 하기 때문에 #1 주소의 Argument 자체가 변경이 되게 됩니다. (call by reference와 call by address의 차이에 대해서는 무시하였습니다.)

call by Value 예시

// 예제 함수입니다.
void func(int x)
{
	x = 2;
}

void CMyFirstProgramDlg::OnBnClickedButton1()
{
	// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
	
	// ↓↓↓ << 실습용 코드가 시작되는 위치 입니다. >> ↓↓↓
	// ------------------------------------------------------

	int i = 1;
	func(i);
	myprint(i);
		
	// ------------------------------------------------------
	// ↑↑↑ << 실습용 코드가 끝나는 위치 입니다. >> ↑↑↑

	UpdateData(FALSE);
}

복사된 값이 Parameter로 전달되었기 때문에 원본은 변경되지 않아서 '1'이라는 결과가 나옵니다.

call by Reference 예시

C++ 스타일

void func(int& x)
{
	x = 2;
}

void CMyFirstProgramDlg::OnBnClickedButton1()
{
	// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
	
	// ↓↓↓ << 실습용 코드가 시작되는 위치 입니다. >> ↓↓↓
	// ------------------------------------------------------

	int i = 1;
	func(i);
	myprint(i);
		
	// ------------------------------------------------------
	// ↑↑↑ << 실습용 코드가 끝나는 위치 입니다. >> ↑↑↑

	UpdateData(FALSE);
}

C 스타일

void func(int *x)
{
	*x = 2;
}

void CMyFirstProgramDlg::OnBnClickedButton1()
{
	// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
	
	// ↓↓↓ << 실습용 코드가 시작되는 위치 입니다. >> ↓↓↓
	// ------------------------------------------------------

	int i = 1;
	func(&i);
	myprint(i);
		
	// ------------------------------------------------------
	// ↑↑↑ << 실습용 코드가 끝나는 위치 입니다. >> ↑↑↑

	UpdateData(FALSE);
}

두 코드 모두 결과는 아래와 같이 동일합니다.

함수는 Argument의 값을 포인터로 원본을 수정하여 결과는 '2'가 되었습니다.

결론

함수 호출 시 Argument의 전달방법 2가지를 비교해 봤습니다. call by value와 call by reference의 차이점을 다른 말로 표현한다면, 함수가 외부의 변수에 영향을 줄 수 있느냐 없느냐의 차이라고 할 수 있습니다. 기능적인 차이점을 바탕으로 용도에 따라서 적절한 방법을 선택하여 사용을 하면 되는데요. 이때 고려해야 할 한 가지 특징이 더 있습니다. 바로 함수의 수행 속도입니다.

구글링을 해 보면 call by reference의 속도적인 이점을 실험적으로 설명한 많은 글들을 쉽게 찾을 수 있습니다. 동작의 방식을 생각해보면 당연한 결과입니다. call by reference는 원본 데이터의 크기에 관계없이 해당 데이터의 포인터만 아규먼트로 전달하기 때문에 실제로 복사되는 크기가 작아서 속도에 이점을 가지게 됩니다. 그리고 함수로 구성했다는 것 자체가 반복 사용을 전재하고 있기 때문에 함수의 사용 빈도가 많아지면 많아질수록 그 차이는 더 커지게 되는 것이죠.

그렇다고 무조건 call by reference를 쓰는 게 좋은가 하면 그렇지만은 않겠죠. 함수라는 것은 논리적 경계를 만듦으로써 코딩 시 작업의 효율을 높이고자 하는데 그 목적이 있기 때문에 이런 특징을 고려하지 않고 남용되는 call by reference는 오히려 프로그램에 치명적인 오류를 야기할 수 있다는 점을 유의해야 하겠습니다. 

 

끝!

반응형

댓글