Archive/C&C++

[C/C++] 함수의 리턴값으로 주소(포인터) 참조자를 받을 시 유의점

novs 2022. 2. 8. 20:00
반응형

*개인적인 공부 내용을 기록하는 용도로 작성한 글 이기에 잘못된 내용을 포함하고 있을 수 있습니다.

#1 Return By Value

#2 Return By Address / Reference


#1 Return By Value

getValue 함수는 값(Value)을 반환하는 간단한 Return-By-Value 함수입니다. Return-By-Value 함수는 복사 과정이 많이 일어나서 비효율적이라는 단점이 있습니다. 물론 아래 코드처럼 간단한 케이스는 상관 없지만 배열이나 구조체 등 데이터가 많은 자료형을 반환할 때는 문제가 발생할 수 있습니다.

 

#include <iostream>
using namespace std;

// return by value
int getValue(int x)
{
	int value = x * 2;
	return value;
}

int main(){
	int value = getValue(3);
	return 0;
}

 

#2 Return By Address / Reference

다음으로 함수의 반환값으로 포인터 즉, 주소를 반환하는 케이스입니다.

#include <iostream>
using namespace std;

// return by address
int* getAddress(int x)
{
	int value = x * 2;
	return &value;
}

 

getAddress 함수의 리턴값을 받는 방법은 2가지를 떠올릴 수 있습니다. 첫 번째는 *(de-referencing) 연산자를 이용해 리턴값을 받는 방법입니다.

int main(){
	int value = *getAddress(5);
	return 0;
}

 

하지만 이 방식은 위험한 생각입니다. 왜냐하면 getAddress 함수의 지역변수 value는 getAddress 함수가 종료되면 메모리가 사라지기 때문입니다. 이처럼 사라질 변수를 de-referencing 하는 것은 큰 위험이 있습니다.

 

실제로 value에 들어있는 값을 출력해 보면 가비지 값(예상치 못한 쓰레기값)이 출력됩니다.

#include <iostream>
using namespace std;

// return by address
int* getAddress(int x)
{
	int value = x * 2;
	return &value;
}

int main(){
	int value = *getAddress(5); // 사라질 변수를 de-referecing 하는 것은 위험이 있음.
 	 cout << value << endl;
	return 0;
}
-831371064

 

혹은 포인터 변수를 이용해 변수의 주소값을 받는 방법을 떠올릴 수 도 있습니다. 하지만 이 방식은 더 위험합니다.

사라질 변수(getAddress의 value)의 주소값을 담고 있는 상황이 되어 버리기 때문입니다.

#include <iostream>
using namespace std;

// return by address
int* getAddress(int x)
{
	int value = x * 2;
	return &value;
}

int main(){
	int *value = getAddress(5);
	return 0;
}

 

참조자(&)의 경우도 마찬가지입니다. 아래처럼 코드를 작성하면 가비지값으로 초기화될 수 있습니다.

//return by refference
int& getRef(int x)
{
	int value = x * 2;
	return value;
}

int main(){
	int value = getValue3(5);
	int &value = getValue3(5); 
	return 0;
}

 

이처럼 리턴값이 임시적으로 생겼다가 사라지는 지역변수인 경우 위에 소개한 방식처럼 리턴하는 것은 예상치 못한 결과를 불러 일으킬 수 있습니다.

 

단, 아래처럼 메모리가 함수의 실행 여부에 영향을 받지 않고, 확실하게 잡혀있는 상태라면 참조자만을 보내는 것은 괜찮습니다.

int& get(std::array<int, 100>& my_array, int ix)
{
	return my_array[ix];
}

int main(){
	std::array<int, 100> my_array;
	get(my_array, 30) = 30;
	return 0;
}

 

* 정리

1. Return By Value 방식은 간편하지만 복사가 많이 일어나 비효율적이라는 단점이 있다. 작은 크기의 데이터를 리턴하는 것은 문제가 되지 않지만 다수의 데이터를 갖고 있는 배열이나 구조체 같은 자료형을 리턴 시에는 문제가 될 수 있다.

2. Return By Address/Reference 방식의 함수에서 리턴값이 임시적으로 생겼다가 사라지는 지역변수인 경우 예상치 못한 결과를 불러 일으킬 수 있다. 단, 메모리가 함수의 실행 여부에 영향을 받지 않고 확실하게 잡혀 있는 상태에서 주소값/참조자를 보내는 것은 괜찮다.

반응형