참조자(Reference)의 이해


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


예제를 통해 살펴보자.


  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. int main() {  
  5.     int num1 = 1020;  
  6.     int &num2 = num1;  
  7.   
  8.     num2 = 3047;  
  9.     cout << "val : " << num1 << endl;  
  10.     cout << "ref : " << num2 << endl;  
  11.   
  12.     cout << "val : " << &num1 << endl;  
  13.     cout << "ref : " << &num2 << endl;  
  14.   
  15.     return 0;  
  16. }  


[ 결과 ]



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

  • 9~10행 : 동일한 값이 출력되면, num1과 num2가 동일한 메모리 공간을 참조함을 증명한다.

  • num1과 num2의 주소값을 출력하게 하였다. 


참조자는 변수를 대상으로만 선언이 가능하다. 하지만 일단 선언되고 나면 변수와 차이가 없다. &연산자를 이용해서 주소값을 반환 받을 수 있고, 함수 내에서 선언된 local 참조자는 지역변수와 마찬가지로 함수를 빠져나가면 소멸된다.


즉 다음의 선언에서,


int &num2 = num1 ;

 

num1이 변수의 이름이라면, num2는 num1의 별칭이라는 뜻이다. 그런데 이 역시 참조자를 이해하기 위한 좋은 비유가 된다. 예를 들어 "송경식"이라는 사람의 별명이 "불곰"이라면 다음 두문장이 의미하는 바와 이 문장을 처리했을 때의 결과는 모두 동일하다.


"송경식에게 전화 해서 당장 오라고 해!"

"불곰한테 전화해서 당장 오라고 해!"


참조자의 특징

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

  • 참조자는 선언과 동시에 누군가를 참조해야만 한다.

  • 참조자는 본디 변수에 또다른 이름을 붙이는 것이기 때문에 상수를 대상으로 참조자를 선언할 수 없다.


참조자는 무조건 선언과 동시에 변수를 참조하도록 해야 한다. 여기서 말하는 변수의 범위는 배열 요소도 포함되며 이와 관련해서는 예제를 통해 알아보자.

  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. int main() {  
  5.     int arr[3] = { 1,2,3 };  
  6.     int &ref1 = arr[0];  
  7.     int &ref2 = arr[1];  
  8.     int &ref3 = arr[2];  
  9.   
  10.     cout << ref1 << endl;  
  11.     cout << ref2 << endl;  
  12.     cout << ref3 << endl;  
  13.   
  14.     return 0;  
  15. }  

[ 결과 ]


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

그리고 포인터 변수 또한 변수이기 때문에 참조자의 선언이 가능하다.


  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. int main() {  
  5.     int num = 12;  
  6.     int *ptr = &num;  
  7.     int **dptr = &ptr;  
  8.   
  9.     int &ref = num;  
  10.     int *(&pref) = ptr;  
  11.     int **(&dpref) = dptr;  
  12.   
  13.     cout << ref << endl;  
  14.     cout << *pref << endl;  
  15.     cout << **dpref << endl;  
  16.   
  17.     return 0;  
  18. }  


[ 결과 ]




  • 10~11행 : 포인터 변수의 참조자 선언도 &연산자를 하나 더 추가하는 형태로 진행이 된다. (9행과 비교)

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

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


참조자(reference)와 함수


C언어를 공부하며 배운 함수의 두가지 호출 방식은 다음과 같다.

  • Call-by-value        ->  값을 인자로 전달하는 함수의 호출방식
  • Call-by-reference   ->  주소 값을 인자로 전달하는 함수의 호출방식

이중에서, Call-by-value 기반의 함수는 다음과 같이 정의된 함수를 의미한다.

  1. int func(int a, int b){  
  2.     return a+b;  
  3. }  

위 함수는 두개의 정수를 인자로 요구한다. 따라서 Call-by-value 기반의 함수이다. Call-by-value 형태로 정의된 함수의 내부에서는, 함수외부에 선언된 변수에 접근이 불가능하다. 따라서 두 변수에 저장된 값을 서로 바꿔서 저장할 목적으로 다음과 같이 함수를 정의하면 원하는 결과를 얻을 수 있다.

  1. void swapp(int a, int b){  
  2.    int tmp = a;  
  3.    a = b;  
  4.    b = tmp;  
  5. }  


위의 함수를 대상으로 다음의 main 함수를 실행해보자.


  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. void swapp(int a, int b);  
  5.   
  6. int main() {  
  7.     int a = 10;  
  8.     int b = 20;  
  9.     swapp(a, b);  
  10.       
  11.     cout << "a : " << a << endl;  
  12.     cout << "b : " << b << endl;  
  13.   
  14.     return 0;  
  15. }  
  16.   
  17. void swapp(int a, int b) {  
  18.     int tmp = a;  
  19.     a = b;  
  20.     b = tmp;  
  21. }  



[ 결과 ]




a와 b에 저장된 값이 서로 바뀌지 않았음을 의미한다. 그래서 필요한 것이 Call-by-reference 기반의 함수이다.


  1. void swapp(int *a, int *b) {  
  2.     int tmp = *a;  
  3.     *a = *b;  
  4.     *b = tmp;  
  5. }  

 

위의 함수에서는 두 개의 주소 값을 받아서, 그 주소 값이 참조하는 영역에 저장된 값을 직접 변경하고 있다.

따라서 위 함수를 대상으로 다음의 main함수를 실행해보자.


  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. void swapp(int *a, int *b);  
  5.   
  6. int main() {  
  7.     int a = 10;  
  8.     int b = 20;  
  9.     swapp(&a, &b);  
  10.       
  11.     cout << "a : " << a << endl;  
  12.     cout << "b : " << b << endl;  
  13.   
  14.     return 0;  
  15. }  
  16.   
  17. void swapp(int *a, int *b) {  
  18.     int tmp = *a;  
  19.     *a = *b;  
  20.     *b = tmp;  
  21. }  


[ 결과 ]




참조자를 이용한 Call-by-reference


Call-by-reference의 가장 큰 핵심은 함수 내에서 함수외부에 선언된 변수에 접근할 수 있다는 것이다.

참조자를 이용해서 함수를 정의해도 이러한 일이 가능하다. 다음의 함수를 살펴보자.


  1. void swpbyref(int &ref1, int &ref2){  
  2.    int tmp=ref1;  
  3.    ref1=ref2;  
  4.    ref2=tmp;  
  5. }  


매개 변수의 선언 위치에 참조자가 와서 햇갈릴 수 있다.


매개변수는 함수가 호출되어야 초기화가 진행되는 변수들이다. 즉 위의 매개변수 선언은 초기화가 이루어 지지 않은 것이 아니라, 함수 호출 시 전달되는 인자로 초기화를 하겠다는 의미의 선언이다. 그럼 위의 함수를 대상으로 다음과 같이 함수를 호출하면 어떻게 될까?


  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. void swpbyref(int &ref1, int &ref2);  
  5.   
  6. int main() {  
  7.     int a = 10;  
  8.     int b = 20;  
  9.   
  10.     swpbyref(a, b);  
  11.     cout << "a: " << a << endl;  
  12.     cout << "b: " << b << endl;  
  13.       
  14.     return 0;  
  15. }  
  16.   
  17. void swpbyref(int &ref1, int &ref2) {  
  18.     int tmp = ref1;  
  19.     ref1 = ref2;  
  20.     ref2 = tmp;  
  21. }  



[ 결과 ]



  • 18~20행 : 두 참조자 ref1, ref2에 저장된 값의 교환 과정이다. 이 과정은 main함수에 선언된 변수 a, b의 교환으로 이루어 진다.

  • 10행 : 매개변수로 참조자가 선언되었으니, 참조의 대상이 될 변수를 인자로 전달하면 된다.


문제


1. 참조자를 이용해서 다음 요구사항에 부합하는 함수를 각각 정의하여라.

->인자로 전달된 int형 변수의 값을 1씩 증가시키는 함수

->인자로 전달된 int형 변수의 부호를 바꾸는 함수


그리고 위의 각 함수를 호출하여 그 결과를 확인하는 main함수까지 작성하여라.


2. 다음의 코드를 살펴보자.


  1. int main(){  
  2.    int num1=5;  
  3.    int *ptr1=&num1;  
  4.    int num2=10;  
  5.    int *ptr2=&num2;  
  6. }  

 

위의 코드를 보면 ptr1과 ptr2가 각각 num1과 num2를 가리키고 있다. 이때 ptr1과 ptr2를 대상으로


swptr (ptr1, ptr2) ;


다음과 같이 함수를 호출하고 나면, ptr1과 ptr2가 가리키는 대상이 서로 바뀌도록 swptr함수를 정의해보자.



문제 1.


  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. int pluss(int a);  
  5. int mul(int a);  
  6.   
  7. int main() {  
  8.     int num1=10;  
  9.     int plusv, mulv;  
  10.     plusv = pluss(num1);  
  11.     mulv = mul(num1);  
  12.       
  13.     cout << plusv << endl;  
  14.     cout << mulv << endl;  
  15.   
  16.     return 0;  
  17. }  
  18.   
  19. int pluss(int a) {  
  20.     int &ref = a;  
  21.     ref = ref + 1;  
  22.     return ref;  
  23. }  
  24. int mul(int a) {  
  25.     int &ref = a;  
  26.     ref = ref*(-1);  
  27.     return ref;  
  28. }  


문제 2.


  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. void swptr(int *(&pref1), int *(&pref));  
  5.   
  6. int main() {  
  7.     int num1 = 5;  
  8.     int *ptr1 = &num1;  
  9.   
  10.     int num2 = 10;  
  11.     int *ptr2 = &num2;  
  12.   
  13.     cout << *ptr1 << endl;  
  14.     cout << *ptr2 << endl;  
  15.   
  16.     swptr(ptr1, ptr2);  
  17.   
  18.     cout << ptr1 << endl;  
  19.     cout << ptr2 << endl;  
  20. }  
  21.   
  22. void swptr(int *(&pref1), int *(&pref2)) {  
  23.     int *ptr = pref1;  
  24.     pref1 = pref2;  
  25.     pref2 = ptr;  
  26. }  


※ 위의 답은 제가 임의로 작성한 답입니다. 여러분이 생각하고 직접 작성해보세요!!


'Language > C++' 카테고리의 다른 글

C++ 기본(2) - 구조체  (0) 2016.12.23
8. C++ 기본(8)  (0) 2016.12.14
6. C++ 기본(6)  (0) 2016.12.13
5. C++ 기본(5) - OOP 단계별 프로젝트 01  (0) 2016.12.12
4. C++ 기본(4)  (0) 2016.12.11

+ Recent posts