danbibibi
article thumbnail
Published 2023. 8. 14. 11:01
[C++] 참조자(reference) 언어/C, C++

참조자(reference)

  • C++에서 다른 변수나 상수를 가리키는 또 다른 방법
  • C 에서는 어떠한 변수를 가리키고 싶을 땐 반드시 포인터를 사용했음
  • 참조자는 가리키고자 하는 타입 뒤에 &를 붙여 표현
  • 참조자는 정의 시에 반드시 누구의 별명인지를 명시해야 함
int& // int형 변수의 참조자
double& // double형 변수의 참조자
int*& // int* (int형 포인터) 타입의 참조자

int& another_a; // 불가능 
int& another_a = a; // 가능

int* p; // 포인터의 경우는 가능

 

  • 한번 어떤 변수의 참조자가 되버린다면, 더이상 다른 변수를 참조할 수 없음
int a = 10;
int &another_a = a; // another_a 는 이제 a 의 참조자!

int b = 3;
another_a = b; // b를 가리키라는 것이 아닌, b의 값을 대입하라는 의미!


// 포인터는 가리키는 대상을 변경 가능
int a = 10;
int* p = &a; // p 는 a 를 가리킴

int b = 3;
p = &b // 이제 p 는 a 를 버리고 b 를 가리킴

 

  • 참조자의 참조자를 만드는 것은 문법상 금지되어 있음
  • 아래의 경우 y와 z 모두 x의 참조자!!
  int x;
  int& y = x;
  int& z = y;

 

함수 인자로 레퍼런스 받기

#include <iostream>

int change_val(int &p) {
  p = 3;

  return 0;
}
int main() {
  int number = 5;

  std::cout << number << std::endl; // 5
  change_val(number);
  std::cout << number << std::endl; // 3
}

 

상수에 대한 참조자

  • 상수 리터럴을 일반적인 레퍼런스가 참조하는 것은 불가능
  •  error : 'initializing' : cannot convert from 'int' to 'int &'
#include <iostream>

int main() {
  int &ref = 4; // error : 'initializing' : cannot convert from 'int' to 'int &'
  // ref = 5; // 이와 같이 리터럴의 값을 바꾸는 말도 안되는 행위가 가능하기 때문!!

  std::cout << ref << std::endl;
}

 

  • 단, 상수 참조자로 선언한다면 리터럴도 참조 할 수 있음
const int &ref = 4;
int a = ref; // a = 4와 동일

 

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

  • 레퍼런스의 레퍼런스,레퍼런스의 배열, 레퍼런스의 포인터는 존재할 수 없음
  • error : 'arr' : arrays of references are illegal
int a, b;
int& arr[2] = {a, b};
문법 상 배열의 이름은 (arr) 첫 번째 원소의 주소값으로 변환이 될 수 있어야 함.
arr[1] 이 *(arr + 1) 로 바뀌어서 처리될 수 있기 때문!!

주소값이 존재 = 해당 원소가 메모리 상에 존재 인데,
레퍼런스는 특별한 경우가 아닌 이상 메모리 상에서 공간을 차지 하지 않음.
따라서 이러한 모순 때문에 레퍼런스들의 배열을 정의하는 것은 언어 차원에서 금지가 되어 있음.

* 레퍼런스가 스코프 밖에서 사용되는 경우, 메모리에 존재

 

  • 배열의 레퍼런스는 가능
  • ref가 arr을 참조 ( ref[0] 부터 ref[2] 가 각각 arr[0] 부터 arr[2] 의 레퍼런스가 됨 )
#include <iostream>

int main() {
  int arr[3] = {1, 2, 3};
  int(&ref)[3] = arr;

  ref[0] = 2;
  ref[1] = 3;
  ref[2] = 1;

  std::cout << arr[0] << arr[1] << arr[2] << std::endl; // 231
  return 0;
}
int arr[3][2] = {1, 2, 3, 4, 5, 6};
int (&ref)[3][2] = arr;

 

레퍼런스를 리턴하는 함수

  • function안에 정의된 a라는 변수의 값이 b에 복사
  • function이 종료되고 나면 a는 메모리에서 사라짐
int function() {
  int a = 2;
  return a;
}

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

 

지역변수의 레퍼런스를 리턴

레퍼런스를 리턴하는 함수에서 지역 변수의 레퍼런스를 리턴하지 않도록 조심해야 함 (Dangling reference)

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

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

// 아래와 동일한 의미
// 원래 참조하고 있던 변수가 이미 사라져 오류가 발생
int& ref = a;
int b = ref; // a가 사라짐

 

외부 변수의 레퍼런스를 리턴

  • function이 리턴한 참조자는 아직 살아있는 변수인 b를 계속 참조
  • c에 b의 값인 5를 대입한 것
int& function(int& a) {
  a = 5;
  return a;
}

int main() {
  int b = 2;
  int c = function(b);
  return 0;
}
참조자를 리턴하는 경우 장점
큰 구조체의 경우, 해당 구조체 변수를 그냥 리턴하면 전체 복사가 발생해서 시간이 오래걸리지만,
해당 구조체를 가리키는 포인터를 리턴한다면 포인터 주소 한 번 복사로 매우 빠르게 동작함.

 

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

  • 원칙상 함수의 리턴값은 해당 문장이 끝나면 소멸
  • 하지만 예외적으로 상수 레퍼런스로 리턴값을 받게 되면, 레퍼런스가 사라질 때 까지 해당 리턴값의 생명이 연장
#include <iostream>

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

int main() {
  const int& c = function();
  std::cout << "c : " << c << std::endl;
  return 0;
}

 

정리

  함수에서 값 리턴 (int f()) 함수에서 참조자 리턴 (int& f())
값 타입으로 받음(int a = f())  값 복사됨 값 복사됨. 다만 지역 변수의 레퍼런스를 리턴하지 않도록 주의
참조자 타입으로 받음 (int& a = f())  컴파일 오류 가능. 다만 마찬가지로 지역 변수의 레퍼런스를 리턴하지 않도록 주의
상수 참조자 타입으로 받음 (const int& a = f())  가능 가능. 다만 마찬가지로 지역 변수의 레퍼런스를 리턴하지 않도록 주의

 

'언어 > C, C++' 카테고리의 다른 글

[C++] Standard Template Library - STL  (0) 2023.08.24
[C++] 객체 지향 프로그래밍  (0) 2023.08.16
[C++] new, delete  (0) 2023.08.14
[C++] namespace  (0) 2023.08.11
[C] 포인터  (0) 2023.06.29
profile

danbibibi

@danbibibi

꿈을 꾸는 시간은 멈춰 있는 것이 아냐 두려워하지 마 멈추지 마 푸른 꿈속으로