[02-3] 참조자(Reference)의 이해

김민성·2022년 7월 11일
post-thumbnail

참조자(Reference)의 이해

변수는 할당된 메모리 공간에 붙여진 이름이다. 그 이름을 통해서 메모리 공간에 접근이 가능하다.
그러면 할당된 하나의 메모리 공간에 둘 이상의 이름을 부여할 순 없을까?

이 때, num2는 num1의 '참조자'가 된다.

여기다가 다음의 문장을 실행하면,

num2 = 3047;

변수 num1의 메모리 공간에 3047이 저장된다.

이렇듯 참조자는 자신이 참조하는 변수를 대신할 수 있는 또 하나의 이름인 것이다.

Referen.cpp

#include <iostream>
using namespace std;

int main(void) {
    int num1=1020;
    int &num2 = num1;

    num2 = 3047;
    cout<<"VAL: "<<num1<<endl;
    cout<<"REF: "<<num2<<endl;

    cout<<"VAL: "<<&num1<<endl;
    cout<<"REF: "<<&num2<<endl;
    return 0;
}
VAL: 3047
REF: 3047
VAL: 0x16d1bb458
REF: 0x16d1bb458

위 예제를 분석해보자.

 int &num2 = num1;

num1에 대한 참조자 num2를 선언하였다. 따라서 이후로는 num1과 num2가 동일한 메모리 공간을 참조하게 된다.

 cout<<"VAL: "<<num1<<endl;
    cout<<"REF: "<<num2<<endl;

동일한 값이 출력되면, num1과 num2가 동일한 메모리 공간을 참조함을 증명하는 셈이다.

  cout<<"VAL: "<<&num1<<endl;
    cout<<"REF: "<<&num2<<endl;

num1과 num2의 주소 값을 출력하게 하였다. num1과 num2가 동일한 메모리 공간을 참조하므로 주소 값 또한 동일하게 출력된다.

참조자는 별칭입니다.

C++에서는 참조자를 다음과 같이 설명한다.

"변수에 별명(별칭)을 하나 붙여주는 것입니다."

즉, 다음의 선언에서,

int &num2 = num1;

num1이 변수의 이름이라면, num2는 num1의 별명이라는 것이다.

참조자의 수에는 제한이 없으며, 참조자를 대상으로도 참조자를 선언할 수 있다.

참조자의 수에는 제한이 없다. 즉 아래의 경우도 가능!

int num1 = 2759;
int &num2 = num1;
int &num3 = num1;
int &num4 = num1;

위의 문장들을 순서대로 실행하면 아래의 그림과 같이 된다.

그리고 참조자를 대상으로 참조자를 선언하는 것도 가능하다.

int num1 = 2759;
int &num2 = num1;
int &num3 = num2;
int &num4 = num3;

이를 실행해도 위의 그림과 같이 된다.

참조자의 선언 가능 범위

참조자는 변수에 대해서만 선언이 가능하다. 즉, 다음의 경우는 불가능.

int &ref = 20; //(x)

상수를 대상으로 참조자를 선언할 수는 없다. 그리고 다음과 같이 미리 참조자를 선언했다가, 후에 누군가를 참조하는 것은 불가능하고, 참조의 대상을 바꾸는 것도 불가능하다.

int &ref; //(x)

또한, 다음과 같이 참조자를 선언하면서 NULL로 초기화하는 것도 불가능하다.

int &ref = NULL; //(x)

참조자는 무조건 선언과 동시에 변수를 참조하도록 해야한다. 그런데 여기서 말하는 변수의 범위에는 배열요소도 포함된다. 다음 예제를 살펴보자.

RefArrElem.cpp

#include <iostream>
using namespace std;

int main(void) {
    int arr[3] = {1, 3, 5};
    int &ref1 = arr[0];
    int &ref2 = arr[1];
    int &ref3 = arr[2];

    cout<<ref1<<endl;
    cout<<ref2<<endl;
    cout<<ref3<<endl;
    return 0;
}
1
3
5

예제와 실행경과에서 보이듯이 배열의 요소는 변수로 간주되어 참조자의 선언이 가능하다.

포인터 변수도 변수이기에 참조자의 선언이 가능하다. 다음의 예제를 살펴보자.

RefPtr.cpp

#include <iostream>
using namespace std;

int main(void) {
    int num=12;
    int *ptr=&num;
    int **dptr=&ptr;

    int &ref=num;
    int *(&pref)=ptr;
    int **(&dpref)=dptr;

    cout<<ref<<endl;
    cout<<*pref<<endl;
    cout<<**dpref<<endl;
    return 0;
}
12
12
12

예제와 실행결과를 분석해보자.

 int *(&pref)=ptr;
 int **(&dpref)=dptr;

포인터 변수의 참조자 선언도 &연산자를 하나 더 추가하는 형태로 진행이 된다.
dptr, ptr 역시 변수이다. 다만 주소값을 저장하는 포인터 변수일 분이다. 따라서 이렇듯 참조자의 선언이 가능하다.

cout<<*pref<<endl;

pref는 포인터 변수 ptr의 참조자이므로, 변수 num에 저장된 값이 출력된다.

cout<<**dpref<<endl;

dpref는 포인터 변수 dptr의 참조자이므로, 변수 num에 저장된 값이 출력된다.

profile
다양한 활동을 통해 인사이트를 얻는 것을 즐깁니다. 저 또한 인사이트를 주는 사람이 되고자 합니다.

0개의 댓글