C++ Chapter 7.3 : 다양한 리턴 값

Date:     Updated:

카테고리:

태그:

인프런에 있는 홍정모 교수님의 홍정모의 따라 하며 배우는 C++ 강의를 듣고 정리한 필기입니다. 😀
🌜 [홍정모의 따라 하며 배우는 C++]강의 들으러 가기!


chapter 7. 함수 : 다양한 리턴 값

1. 값을 리턴

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

int main()
{
    int value = getValue(3);

    return 0;
}
  1. getValue(3)
    • 함수가 호출되고 인수 3이 넘어가면서 int getValue 함수의 매개변수인 int x에 3이 복사된다.
      • 내부적으로 int x = 3 과정이 이루어짐
      • getValue 함수의 지역변수인 value (값 6)가 리턴된다.
  2. int value = getValue(3);
    • getValue의 리턴값인 value의 값이 main의 value복사된다.
    • 그리고 getValue가 역할을 다하고 끝내면서 지역변수인 value도 메모리에서 사라진다.
  • int 리턴 + int value = getValue(3);
    • 리턴을 이렇게 으로 하면 대규모로 진행할시 느려질 수 있다.
      • 복사가 많아지니까!
      • 구조체, 클래스, 배열 사용시에는 비효율적인 방법이다.
    • 변수 하나를 리턴하는 경우라면 값을 리턴하는 것이 효율적이고 안전하다.


2. 주소를 리턴

int * allocateMemory(int size)
{
    return new int[size];
}

int main()
{
    int * array = allocatememory(1024);

    delete [] array;

    return 0;
}
  1. allocatememory(1024)
    • 함수가 호출되고 인수 1024가 넘어가면서 int * allocateMemory 함수의 매개변수인 int size에 1024가 복사된다.
      • 내부적으로 int size = 1024 과정이 이루어짐
  2. int * array = allocatememory(1024);
    • allocatememory의 리턴값인 new int[size]의 값이 main의 int * array에 대입된다.
      • new는 동적 할당 메모리의 주소값을 리턴하므로 주소값이 main의 int * array에 대입된다.
      • new는 힙메모리에서 동적 할당 받기 때문에 함수 범위가 끝나도 사라지지 않는다. 동적 할당 메모리는 프로그래머가 직접 delete로 반납하기 전까지는 계속 살아있음.
    • 함수 호출이 끝나고 리턴이 다 끝나도 new로 동적 할당 받은 힙 메모리는 유지됨
    • 지역 변수인 size만 사라짐
int * getValue(int x)
{
    int value = x * 2;
    return &value;
}

int main()
{
    int * value_1 = getValue(3);
    int value_2 = *getValue(3);

    return 0;
}

return &value; : 이렇게 주소값 리터럴 (R-value)로 리턴하는 경우

  • value는 지역변수이므로 리턴 후 메모리가 해제되고 사라지기 때문에 이미 사라진 곳의 주소를 넘기게 되는 것일 수 있어 위험할 수 있다. 사라진 메모리의 주소를 복사받는 것이니까.
    • int * value_1 = getValue(3);
      • 메모리가 사라진 값의 주소를 받음
    • int value_2 = *getValue(3);
      • 메모리가 사라진 값의 주소를 간접참조함
  • 따라서 포인터를 리턴할시 위와 같이 코드를 짜는것은 지양하자.
  • int * array = allocatememory(1024); 같은 경우는 동적 할당 받아 사라지지 않는 힙메모리를 사용한 것이니 함수가 끝나면서 사라질 위험이 없기 때문에 안전한 것.
  • 이처럼 함수 내에서 선언된 지역변수를 반환하는 경우에는 ***주소 리턴***을 받지 말자. 위험함.


3. 참조를 리턴

그냥 int 로 int & 참조 리턴을 대입 받는 경우

int & getValue(int x)
{
    int value = x * 2;
    return value; // value의 참조를 리턴
}

int main()
{
    int value = getValue(3);

    return 0;
}
  1. getValue(3)
    • 함수가 호출되고 인수 3이 넘어가면서 int getValue 함수의 매개변수인 int x에 3이 복사된다.
      • 내부적으로 int x = 3 과정이 이루어짐
      • getValue 함수의 지역변수인 value (값 6)가 리턴된다.
  2. int value = getValue(3);
    • main의 valueint & getValue에서 리턴한 value가 참조하는 메모리를 복사 받게 된다!
      • int & value가 아닌 그냥 int value로 함수 리턴값을 대입 받아서 참조가 아닌 그냥 리턴값을 복사한게 되는 것.
      • 이 함수는 리턴타입이 int &이 되므로 value에 대한 참조를 리턴한다.
    • 그냥 int, 즉 값으로 리턴하면 return value더라도 value의 값, 즉 R-value로서 리턴을 하게 되는데
    • int &, 즉 참조로 리턴하면 L-value로서 리턴을 하게 된다. - 그러나 getValue가 역할을 다하고 끝내면서 지역변수이자 참조 변수인 value가 참조하는 메모라 공간은 잠깐 있다 사라진다. - main의 value없어진 공간을 복사 받은 셈이 되는 것이다
    • 그래서 실행하면 warning: reference to local variable ‘value’ returned [-Wreturn-local-add] 같은 경고메세지가 뜸.

int & 참조 변수로 int & 참조 리턴을 대입 받는 경우

int & getValue(int x)
{
    int value = x * 2;
    return value; // value의 참조를 리턴
}

int main()
{
    int & value = getValue(3);

    cout << value << endl; // 6으로 잘 나온다.
    cout << value << endl; // 쓰레기 값이 나온다.

    return 0;
}

int & value = getValue(3);

  • getValue 함수의 지역변수 value의 메모리 공간을 참조 변수 int &인 value가 참조하게 된다.
  • 함수가 종료되며 지역 변수인 value 메모리 공간은 파괴된다.
  • 그러나 !! main의 int & 변수인 value는 계속해서 이 파괴되어 쓰레기 값 들어있는 공간을 참조한다.
    • 따라서 한번 더 value를 출력하려하면 쓰레기값이 나오는 것. 사라져버려서 ㅠ ㅠ
  • 이처럼 함수 내에서 선언된 지역변수를 반환하는 경우에는 ***참조 리턴***을 받지 말자. 위험함. 주소 리턴과 마찬가지로 !
int & get(std::array<int, 100> & my_array, int ix)
{
    return my_array[ix];
}
int main()
{
    std::array<int, 100> my_array;
    my_array[30] = 10;

    get(my_array, 30) = 1024; // my_array[30] = 1024 과 동일
}
  • 인수를 기존에 main에서 메모리를 잡고 있는 상태인 배열의 참조로 넘긴 상태. std::array<int, 100> & my_array
    • 따라서 get 내의 지역변수 my_array는 함수가 끝나고 사라져도 공간은 사라지지 않고 계속 존재하기 때문에 기존 공간을 참조 매개변수에게 넘기고 이를 리턴받을 경우엔 위험할 일이 없다.
  • get(my_array, 30) = 1024;
    • 참조로 리턴했기 때문에, 즉 L-value로서 리턴했기 때문에 변수에 값을 저장하는 것 처럼 보인다.
      • my_array[30] = 1024 과 동일

정리

  1. int 값 리턴 : 임시 변수에 리턴된 객체를 복사한 후 그 임시 변수를 R-value 속성으로 리턴한다.
    • 즉, 해당 객체가 아닌 주소가 다른 임시 변수의 메모리가 리턴 된다.
  2. int & 참조 리턴 : 임시 변수에 복사하는 과정 없이 그 객체를 레퍼런스, 즉 실제 객체 그 자체를 L-value 속성으로 리턴한다.
    • 리턴하는게 단순 함수 내의 지역변수라면 소멸 시점을 주의하여야 한다.
    • 함수가 소멸되면서 지역변수도 소멸되는데 지역 변수를 참조로 리턴 받은 바깥 변수는 여전히 소멸된 지역변수 메모리 자리를 참조하고 있는 것이 되기 때문이다.


4. 구조체를 리턴

using namespace std;

struct S
{
  
  int a, b, c, d;
};

S getStruct()
{
    S my_s{1, 2, 3, 4};
};

int main()
{
    S my_s = getStruct();
    cout << my_s.b << endl;

    return 0;
}
  • 구조체를 리턴받아 값을 받을 경우 한번에 여러개의 값을 받을 수 있다.
  • 함수를 하나 만들 때마다 구조체 또한 또 만들어야 하는게 단점
    • 튜플(Tuple)로 극복할 수 있다.


5. 튜플을 리턴

#include <tuple>

using namespace std;

std::tuple<int, double> getTuple() 
{
    int a = 10;
    double d = 3.14;
    
    return std::make_tuple(a, d);
}
int main()
{
    std::tuple<int, double> my_tp = getTuple();
    cout << std::get<0>(my_tp) << endl; // a. int다
    cout << std::get<1>(my_tp) << endl; // b. double이다.

    return 0;
}
  • #include <tuple>
    • std::tuple
    • std::make_tuple
  • 튜플은 대괄호 []를 쓴다.
  • 튜플을 통해 각각 자료형이 다른 두개 이상의 리턴값을 받을 수 있다.
  • C++ 17부턴 std::tuple<int, double> my_tp = getTuple(); 을 간단하게 auto[a, d] = getTuple 이런식으로 쓸 수 있다.


🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우 
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄

맨 위로 이동하기

댓글남기기