본문 바로가기

문과 코린이의, [C. C++] 기록/C++ 이론

[문과 코린이의 IT 기록장] C,C++ - 레퍼런스(&) (참조자, 함수 인자로 레퍼런스 받기, 참조자의 참조자? , 상수에 대한 참조자, 레퍼런스의 배열과 배열의 레퍼런스, 레퍼런스를 리턴하는 함수)

반응형


 

 1. 참조자 (reference)

- C++에 새로 도입되는 새로운 개념

 * C언어에서는 어떠한 변수를 가리키고 싶을 때는 반드시 포인터를 사용해야 한다.

 * 그렇지만 C++에서는 다른 변수나 상수를 가리키는 방법으로, 또 다른 방식인 참조자(ref)를 제공한다.

- 참조자를 사용하면 포인터에 비해, 불필요한 &와 *의 사용이 없다. 따라서 훨씬 더 코드를 간결하게 나타낼 수 있다

 

- 참조자란 '또 다른 이름(별명)' 이라고 이해하면 된다. 

 

ex )

- int & another_a = a; // another_a는, int형 변수 a의 참조자이다.

- another_a = 5; // another_a는 a와 같은 것이므로, a의 값도 같이 변경된 것을 볼 수 있다.


1) 참조자(레퍼런스) 정의 방법 

- 가르키고자 하는 타입 뒤에 &를 붙이면 참조자 정의가 가능하다.

ex ) int& : int형 변수의 참조자

     doule& : double형 변수의 참조자

     int*& : int*와 같은 포인터 타입의 참조자

 

- 참조자는 처음에 누구의 별명이 될 것인지 지정되어야 한다.

ex ) int & another_a; // 오류 발생


2) 참조자(레퍼런스) 주의사항

- 어떤 한 변수의 참조자가 되어버리면, 더이상 다른 변수를 참조할 수 없게 된다.

- 즉, another_a = b; 이 문장은, a = b; 와 같은 이야기이다.

- 따라서 &another_a = b; 이러한 문장은 실행될 수 없다.


 

 

 2. 함수 인자로 레퍼런스 받기

int change_val(int &p) { 

//그냥 정의하는 int&p는 이루어질 수 없지만, 이는 함수가 호출될 때 정의되므로, 결과적으로 int&p = number;가 실행되는 것과 같다고 생각하면 된다.

    p = 3; // number = 3과 같은 의미

    return 0;

}

 

 


 

 3. 참조자의 참조자? 

ex )

int &y = x; // x의 참조자로 y를 정의함. (즉 x = y)

int &z = y; // z는 y의 참조자이다? 참조자의 참조자인가?

- 위의 내용에서 참조자의 참조자는 만들 수 없도록 금지되어 있다고 했다. 그리고 자세히 살펴보면 위의 내용은, 결국 x의 참조자를 선언해라와 같은 의미가 되서, z역시 x의 참조자가 될 것이다.

 


 

  4. 상수에 대한 참조자

int & ref = 4;

// 이 상수 값 자체는 리터럴이기 때문에 X

cf) 만약 ref = 5; 라는 행동을 하면, 리터럴인 4의 값이 5로 바뀐다는 것이니까 불가능함.

 

const int &ref = 4; // 상수 참조자로 선언한다면, 리터럴도 참조할 수 있다. (값이 변화하지 않기 때문에)

 


 

 

 5. 레퍼런스의 배열과 배열의 레퍼런스

1) 레퍼런스의 배열

- 레퍼런스의 배열불가능하다

 ex ) int & arr[2] = {a, b}; (X)

 

* 일반적으로 배열의 특성을 생각해보면, arr[1]과 같은 문장이 *(arr+1)으로 변경되어 처리된다.

* 즉, 주소값이 존재한다는 것인데, 이는 메모리 상에 원소가 공간을 차지하고 있다는 의미와 같다. 

* 그러나 레퍼런스는 특별한 경우가 아닌 이상 메모리에서 공간을 차지하지 않는다. 따라서 C++차원에서는 금지되어 있다.


2) 배열들의 레퍼런스

(1) 일차원 배열의 레퍼런스

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

int (&ref)[3] = arr;

// ref가 arr을 참조하도록 함.

// 크기가 3인 int 배열의 별명(참조자)

 

 

(2) 이차원 배열의 레퍼런스

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

int (&ref)[3][2] = arr;

// 일차원 배열과 동일함

 


 

 

 6. 레퍼런스를 리턴하는 함수

1) 기본 함수와, 반환, 복사

int b = function()

// a라는 변수의 값이 b에 복사된다.

* function이 종료되고 나면 a는 메모리에서 사라지게 된다, 따라서 main안에는 a를 만날 일이 없다.


2) 지역 변수의 레퍼런스 반환

int & function() { // function()의 반환형은 int&이다. 즉, 참조자를 반환하게 된다.

   int a = 2;

   return a; // 그러나 이 함수에서 정의되어 있는 a는, 함수의 리턴과 함께 사라지게 된다.

}

 

따라서 main에 있는 int b = function(); 이 문장은 아래의 뜻과 같다.

int & ref = a; // 참조자로 반환하므로, ref가 a와 같은 의미가 됨.
// 반환하면서 a가 사라짐
int b = ref; // (?)

이처럼 레퍼런스를 리턴하는 함수에서는, 지역 변수의 레퍼런스를 리턴하지않도록 조심해야 한다.


3) 외부 변수의 레퍼런스 반환

int & function(int &a) {

// 인자로 받은 레퍼런스를 그대로 리턴하고 있다.

// function(b)를 실행한 시점에서, int &a = b가 된다.

// 따라서 function이 반환한 참조자는, 결국 int &ref = int &a = b가 되어서, 계속 살아있는 변수인 b를 참조한다.

 

 

[ 참조자를 리턴하는 경우의 장점은? ]

- C언어에서 엄청나게 큰 구조체가 있을 때, 해당 구조체 변수를 그냥 리턴하면, 전체 복사가 발생되게 되면서 시간이 오래걸린다. 그러나 해당 구조체를 가리키는 포인터를 리턴한다면, 포인터 주소 한번 복사로 매우 빠르게 끝난다는것을 배웠다.

- 이와 마찬가지로, 레퍼런스를 리턴하게 된다면, 레퍼런스가 참조하는 타입의 크기와 상관 없이, 딱 한번의 주소값 복사로 전달이 끝나게 되어 매우 효율적이다.


4) 참조자가 아닌 값을 리턴하는 함수를, 참조자로 받기.

ex 1 )

int & c = function() // 컴파일 오류 발생

- 함수의 리턴값은 해당 문장이 끝나고 바로 사라지는 값이기 때문에, 이와같이 참조자를 만들게 되면 바로 다음에 문제가 발생한다. 즉, 이후 c=2; 와 같은 코드를 작성하게 된다면 런타임 오류가 발생할 것이다.

 

ex 2 ) 예외 규칙

const int & c = function(); // 문제 없이 컴파일 가능

- 원칙상 함수의 반환값은 해당 문장이 끝나면 소멸되는것이 정상이다.

- 그러나, 예외적으로 상수 레퍼런스로 값을 받게 되었을 때는, 해당 리턴값이 레퍼런스가 사라질 때 까지 존재한다.


[ 총 정리 ]

  int function(){...} [함수에서 값 리턴] int & function(int & a){...}
[함수에서 참조자 리턴]
int a = function();
[값 타입으로 받음]
값 복사 값 복사
* 지역 변수의 레퍼런스를 리턴하지 않도록 주의
int & a = function();
[참조자 타입으로 받음]
컴파일 오류 가능
* 지역 변수의 레퍼런스를 리턴하지 않도록 주의
const int & a = function();
[상수 참조자 타입으로 받음]
가능 가능
* 지역 변수의 레퍼런스를 리턴하지 않도록 주의

 


* 유의사항
- 아직 공부하고 있는 문과생 코린이가, 정리해서 남겨놓은 정리 및 필기노트입니다.
- 정확하지 않거나, 틀린 점이 있을 수 있으니, 유의해서 봐주시면 감사하겠습니다.
- 혹시 잘못된 점을 발견하셨다면, 댓글로 친절하게 남겨주시면 감사하겠습니다 :)
반응형