---
description: 참조자와 포인터의 차이점, 그리고 댕글링 레퍼런스의 위험성과 그것을 해결하는 방법 중 하나인 Const 참조자에 대한 정리.
---

# \[CPP-01] 참조자(reference)와 포인터는 다르다

**\[CPP-Module01 / ex04: HI THIS IS BRAIN 과제]**

* Make a program in which you will create a `string` containing "HI THIS IS BRAIN", a `pointer` to it, and a `reference` to it.
* You will then **display it using the pointer**, and finally **display it using the reference**. That’s all, no tricks.

### 고민한 지점

1. CPP 참조자 사용법
2. 참조자와 포인터의 차이점

## 1. 참조자(reference)는 포인터와 다르다

아래 예제와 같이 가리키고자 하는 `자료형` 뒤에 `&` 기호를 붙여 참조자를 정의할 수 있다.

```cpp
std::string str = "HI THIS IS BRAIN";
std::string *ptr = &str;
std::string& ref = str;
```

출력되는 값을 봤을 때 `*ptr` 과 `ret`는 동일하게 취급된다. 실제로 참조형은 내부적으로 포인터를 사용하여 컴파일러에서 구현된다고 한다.

그러나 포인터와 참조자는 **아예 다른 개념**이라고 생각하는게 이해에 도움이 된다. **1. 참조형은 선언과 동시에 유효한 객체로 초기화** 해야한다. 그리고 **2. 한번 초기화되면 절대 다른 객체를 참조할 수 없으므로** 포인터보다 안전하다. 포인터는 언제든지 다른 변수의 주소를 가리킬 수 있다.

* 포인터 : 객체의 **주소**를 가리키는 변수
* 참조자 : 객체의 **값**을 담는 공간&#x20;

그래서 포인터는 `*`나 `->`로 역참조를 해야 값을 가져올 수 있지만 **3. 참조자는 `&`만 빼고 그대로 쓰면 값을 참조**할 수 있다. 참조형은 참조된 객체의 또다른 이름(별명)이라고 생각해도 된다. 참조형 변수의 값을 연산하면 참조된 객체의 값도 변경된다.

## 2. 참조자 취급 주의: 댕글링 레퍼런스

* `l-value` : 메모리 주소를 가진 객체
* `r-value` : 메모리 주소가 없고, 표현식 범위에만 있는 임시 값. _ex) 지역변수, 대입연산자 오른쪽에 오는 값_

여기서 참조자의 특징이 나타난다. 참조자는 `NULL`, `const`, `r-value` 값으로 초기화할 수 **없다**. **없다!** 이 경우 장점이 있다. 함수의 매개변수로 참조자를 사용하면, 매개변수로 NULL이 들어왔을 때를 예외처리하지 않아도 될 것이다.

**하지만 문제도 있다.** 아래 예제를 보자.

```cpp
// 댕글링 레퍼런스 1

int& function() {
  int a = 2;
  return a;
}

int main() {
  int b = function();
  b = 3;
  return 0;
}
```

function 함수는 a의 참조자를 리턴한다. 하지만 이 때 **a는 함수가 끝나면 사라지는 r-value값**이다. 따라서 a는 참조자가 될 수 없다. 이미 사라진 변수를 참조하려고 하니 에러가 날 수밖에 없는 것이다. 이런 참조자를 `댕글링 레퍼런스` 라고 부른다.

아래 예제도 마찬가지로 문제가 있다.

```cpp
// 댕글링 레퍼런스 2

int function() {
  int a = 5;
  return a;
}

int main() {
  int& c = function();
  return 0;
}
```

변수 c의 참조자도 r-value로 초기화를 시도하고 있다. 영원히 짝궁을 만나지 못하고... 에러가 날 것이다.

댕글링 레퍼런스를 해결하는 방법이 두 가지 있다.

### 방법 1. 매개변수로 참조자를 받아 참조자를 그대로 리턴

```cpp
// 댕글링 레퍼런스 1의 해결방법

int& function(int& a) { //매개변수로 참조자를 받아 전달된 인수를 수정할 수 있다.
  a = 5;
  return a;
}

int main() {
  int b = 2;
  int c = function(b);
  return 0;
}
```

a 자체가 이미 l-value인 b의 참조자이기 때문에 문제가 생기지 않을 것이다.

> 만약 b가 아니라 function(2)와 같이 직접 값을 넣었다면?
>
> 에러가 난다. non-const 참조자는 const 값으로 초기화 할 수 없기 때문이다.

### 방법 2. const 참조자 사용

위 예제에서는 전부 참조자가 non-const 참조자였다. CPP에서는 `const` 참조자를 지원한다. const 참조는 유용하다. **non-const 값, const 값 및 r-value로 초기화 할 수 있기 때문이다.** 참조하는 객체가 const가 아니더라도 **const로 간주**한다.

이 경우 어떤 장점이 있을까?

원래라면 r-value 값이 생성된 표현식 끝에서 소멸되겠지만, const로 간주된다면 상수형 변수의 특성처럼, **참조자가 사라질 때 까지 r-value 값의 수명이 연장**된다. 위의 댕글링 레퍼런스2의 문제는 따라서 const 참조자를 사용하는 것으로 해결할 수 있다.

```cpp
// 댕글링 레퍼런스 2의 해결방법

int function() {
  int a = 5;
  return a;
}

int main() {
  const int& c = function(); // const 참조자 사용
  return 0;
}
```

## 3. const 참조자를 매개변수로 받는 함수

CPP에서 const 지시자는 용도가 명확한 만큼 자주 사용된다. 이전 글에서 [const 함수](https://velog.io/@hidaehyunlee/CPP-%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5%EC%9D%98-%EA%B4%80%EC%A0%90%EC%9C%BC%EB%A1%9C-%ED%81%B4%EB%9E%98%EC%8A%A4-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0)에 대해 다뤘던 것처럼, 만약 const 참조자를 매개변수로 받은 함수가 있다면, **함수 내부에서 참조된 변수의 값을 변경할 수 없다.**

```cpp
int& function(const int& a) {
  a = 5; //not allowed, a is const
  return a;
}
```

CPP에서 `const`를 활용하는 더 다양한 방법은 다음 과제에서 배울 수 있을 것 같다.
