C++ 참조형 변수

C++ 2019.11.22 00:00

참조형 변수 (Reference variable)
C언어에서 일반 변수와 포인터 변수에 대해서 학습했는데 변수란 메모리 공간에 할당된 이름이다. 그 이름을 통해 해당 메모리 공간에 접근한다. C언어에서는 하나의 메모리 공간에 하나의 이름만 부여할 수 있다.

하지만 C++ 에서는 reference 를 선언하게 되면, "이름이 존재하는 공간에 하나의 이름을 더 부여" 할 수 있다.


reference 를 참조형 변수, 참조 변수 또는 참조자 라고 표현한다. 책에 따라 우리말 명칭이 약간씩 다름을 알자.


reference는 C++에서 지원하는 변수 타입으로 다른 객체 또는 값의 별칭으로 사용된다.

int a(10); // C언어의 int a = 10; 과 같다.

int &ref = a; // 선언 방법은 자료형 &참조변수이름 = 원래변수;

non-const 값 참조형은 자료형 뒤에 앰퍼샌드(&)를 사용하여 선언한다.
ㅇ l-value는 메모리 주소를 가진 객체이고 r-value는 메모리 주소가 없고, 표현식 범위에만 있는 임시 값이다.

ㅇ 참조변수에 대입하는 값은 l-value 만 가능하다.


참조자는 별도의 메모리를 할당하지 않고 변수 선언문에서 초기화에 사용한 변수의 메모리를 참조하여 사용한다. 이러한 이유로 참조자는 선언과 동시에 무조건 변수를 참조하도록 해야 한다.

null 값을 저장할 수 있는 포인터와 다르게, null 참조 같은 것은 없다.


배열의 요소는 변수로 간주되어 참조자 선언이 가능하다.

int arr[3] = { 1, 2, 3};

int &ref1 = arr[0];

int &ref2 = arr[1];

int &ref3 = arr[2];


포인터 변수도 변수이기 때문에 참조자의 선언이 가능하다.

int num = 10;

int *ptr = #

int &ref = num;

int *(&pref) = ptr;


#include <iostream>

using namespace std;

void swap(int& a, int& b) { // 함수의 매개변수를 참조변수로 선언하는 방식
    // 참조변수로 매개변수를 받았다
    int tmp = a;
    a = b;
    b = tmp;
}

int main() {
    int a(10);
    int b(5);

    swap(a, b);

    std:cout << "두 수의 값을 바꾸는 함수" << std::endl;
    std::cout << "a = " << a << std::endl;
    std::cout << "b = " << b << std::endl;

    int arr[10] = {1,2,3,4,5,6,7,8,9,10};

    for (int n : arr) {
        cout << n << ' ';
        n++;
    }
    cout << endl;

    // reference 변수

    // 자료형 &참조변수이름 = 원래변수;
    int &ref = a; // 변수를 입력받는다. 상수는 안된다.
    ref = 20;

    cout << "ref : " << ref << endl;
    cout << "a : " << a << endl;

    ref++;
    // reference 연산은 reference가 참조하는 변수의 이름으로 하는 연산과 효과가 똑같다.
    cout << "ref : " << ref << endl;
    cout << "a : " << a << endl;

    string food = "Pizza"; // food 변수
    string &meal = food; // food 의 reference

    cout << endl;
    cout << food << endl;  // Pizza 출력
    cout << meal << endl;  // Pizza 출력

    string* ptr = &food; // 포인터 선언 (주소값을 입력 받는다)

    cout << endl;
    cout << &food << endl;  // food 값의 메모리 주소 출력
    cout << ptr << endl;
    cout << *ptr << endl; // 역참조 연산자(*)로 포인터가 가리키는 값 출력

}


예제2

#include <iostream>
using namespace std;

int& increment(int& val) { // 참조 변수로 원본을 받음
    val++;
    return val;
}

int main() {

    int n = 10;
    int& ref = increment(n); // 파라미터로 reference 원본 넘겨줌

    cout << "n : " << n << endl;
    cout << "ref: " << ref << endl;

}

원본 변수 n 은 참조 변수 ref가 사용되는 동안 유지되기 때문에 결과는 11을 반환한다.


그런데 return 받는 변수가 지역변수라면 어떤 결과가 발생할까?

https://www.youtube.com/watch?v=Bko1OoExWhg&t=319s 동영상 강좌를 참조해서 들어보자.


#include <iostream>
using namespace std;

int &CalSum(int s, int e) {
    int sum = 0;
    for (; s <= e; ++s) {
        sum += s;
        cout << " sum : " << sum << endl;
    }

    cout << " sum address : " << &sum << endl;
    return sum;
}

int main() {

    int &ref1 = CalSum(1, 20);
    int &ref2 = CalSum(1, 10);

    // cout << endl; // 이 한줄을 넣으면 결과가 엉뚱하게 나온다.
    cout << " ref1: " << ref1 << ", ref2: " << ref2 << endl;

    return 0;
}

실행 결과

C++ 에서 함수 처리 return 메커니즘은 return 값을 임시 저장 공간에 복사한 후 그것을 return하는 방식이다.
하지만 return by reference 형식을 사용할 경우 임시 저장공간을 사용하지 않고 호출 함수가 return값에 직접 접근을 하게 된다.


main()이 실행되면서 int ref1 과 int ref2 에 메모리를 각각 4 바이트씩 할당한다.

그 다음에 CalSum() 함수가 실행되면 함수 내 지역변수 int sum 에 메모리를 할당하기 위해 추가적으로 4바이트를 할당한다.


CalSum(1, 20) 의 결과 리턴으로 210을 반환한다. 그런 다음에 CalSum()함수가 종료되고, 지역변수 sum 이 차지하는 메모리 공간은 사라진다.

CalSum(1, 10)을 실행하면서 다시 지역변수 sum 이 차지하는 메모리 공간을 할당한다. 할당된 공간의 메모리 주소는 동일한 주소 공간을 차지한다는 걸 위 실행 결과에서 확인할 수 있다.

여기서 ref1 도 동일한 메모리 주소를 참조하고 있기 때문에 결과는 210이 아닌 55를 반환한다.


그 이유는 함수 내의 로컬 변수의 reference를 참조할 경우, local 변수는 함수 종료시 파괴되지만 ref1의 경우 reference 자체를 계속해서 참조하고 있기 때문이다.


따라서 지역변수를 reference 로 return 하는 경우 원하지 않는 결과를 초래할 수 있으므로 코딩시 주의해야 한다.

블로그 이미지

Link2Me

댓글을 달아 주세요