본문 바로가기

C++

레퍼런스(Reference)와 포인터(Pointer) 차이

1. 포인터는 자신이 가리키는 주소 값을 언제든 변경할 수 있고 레퍼런스는 초기화할 때 반드시 값을 할당해줘야 한다. 

int main()
{
    int number_1 = 10;
    int number_2 = 20;

    // 주소 값 변경이 가능하다.
    int* pI = &number_1;
    pI = &number_2;

    int &refI;              // 에러 : 참조(레퍼런스) 변수는 초기화가 필요하다.
    int &refI = number_1;	
    refI = number_2;        // number_1의 참조가 되는것이 아닌, number_2의 값이 들어간다.

    return 0;
}

 

2. 포인터는 운영체제에 따라 4, 8바이트 메모리 크기, 레퍼런스는 자신이 참조하는 메모리의 크기를 가지게 된다. 

class TestClass
{
public:
    int m_iData;
    int m_iArrData[5];

public:
    void RefCallFunction(const TestClass& _ref)
    {
        size_t size = sizeof(_ref);
        cout << "size : " << size << endl;
    }

    void PointerCallFunction(const TestClass* _ptr)
    {
        size_t size = sizeof(_ptr);
        cout << "size : " << size << endl;
    }
};

int main()
{
    TestClass cls = TestClass();
    cls.RefCallFunction(cls);
    cls.PointerCallFunction(&cls);

    return 0;
}

결과
size: 24 (TestClass 크기) - RefCallFunction()
size : 4 (x86 4바이트) - PointerCallFunction()

 

3. 포인터는 nullptr로 할당(초기화)할 수 있지만 레퍼런스는 nullptr로 할당할 수 없다.

int* p = nullptr;
int& ref = nullptr;      // 에러 : 비const 참조에 대한 초기 값은 lvalue여야 한다.

int a = 3;
int b = 4;
int& ref2 = a + b;       // 에러 : 비const 참조에 대한 초기 값은 lvalue여야 한다.
const int& ref3 = a + b; // 컴파일 가능

rvalue? 우측값은 대입 시에 오직 오른쪽에만 오는 식. 상수(const)라고 볼 수 있다.

레퍼런스 형태로 rvalue 타입의 값을 담기 위해서는 상수에 대한 참조자 형식을 사용해야 한다.

 

4. 포인터는 배열 형식으로 접근할 수 있으며, 포인터에 + 연산을 이용하여 다음 메모리로 접근할 수 있다.

int arr[5] = { 0, 1, 2, 3, 4 };
int* pArr = arr;

// 주소 값
cout << "arr[0] Adress : " << pArr << endl;
cout << "arr[3] Adress : " << pArr + 3 << endl;

// * 키워드를 이용한 역참조
cout << "arr[0] value : " << *pArr << endl;
cout << "arr[3] value : " << *(pArr + 3) << endl;

 

5. 포인터는 메모리 주소를 담는 변수, 레퍼런스는 자신이 참조하는 메모리의 주소로 된다.

 

6. 레퍼런스의 배열과 배열의 레퍼런스 차이

 

- 레퍼런스의 배열

int a, b;
int& arr[2] = { a, b } ;

위 코드 실행 시 컴파일 에러 

구글링! 결과 C++ 규정에서는 There shall be no references to references,no arrays of references, and no pointers to references (레퍼런스의 레퍼런스,레퍼런스의 배열, 레퍼런스의 포인터는 존재할 수 없다.)

왜? 5번에서 레퍼런스는 자신이 참조하는 메모리의 주소로 대체된다고 했는데, 위 코드의 arr은 배열의 시작주소가 된다.

메모리의 주소를 담기 위해서는 포인터 자료형이 필요하다. 레퍼런스 형태의 배열은 모순이된다.

 

- 배열의 레퍼런스

int a, b, c;
int arr[3] = { a, b, c};
int(&ref)[3] = arr;

// 포인터로 사용하는 경우
int *pArray = arr;

&ref는 array[0], [1], [2] 값을 ref[0], ref[1], ref[2]로 참조할 수 있다.  하지만 이렇게 사용하는 경우는 거의 없다. 

이유로는 배열의 크기가 같아야하며, 위 코드는 포인터로 사용할 경우 더 쉽고 편하게 사용할 수 있기 때문이다.