-
1. 참조자 사용법
#include<cstdio> #include<string> int _tmain(int argc, _TCHAR* argv[]) { // (int &) => 참조 자료형 / (int *) => 포인터 자료형 int a = 0; // 참조 자료형(참조형) 을 가지는 변수의 선언 int& i = a; // 포인터 자료형을 가지는 변수 선언 int* ptr_i = &a; return 0; }
위의 코드는 참조형 (참조 자료형의 변수) 와 포인터 (포인터 자료형의 변수)를 각각 생성한 것이다. 아래 자료를 보면서 차이를 확인해 보자
변수 a => 주소 0x 0082FEFC
참조자 i => 주소 0x 0082FEFC
포인터 prt_i => 주소 0x 0082FEE4 이며 해당 주소의 메모리의 값은 0082FEFC 이다.
참조 자료형은 참조자(참조 자료형의 변수) 의 우측에 오는 피연산자 즉 r-value 를 참조한다는 의미이다. 즉 r-value 를 참조 함 에 따라 r-value 와 같은 주소를 가지게 된다. 쉽게 말해 참조자는 r-value 의 또다른 식별자 (이름) 가 되는 것이다.
포인터 자료형은 포인터 (포인터 자료형의 변수) 의 내부에 있는 값을 주소로 인식하여 ' * ' 간접지정 연산자를 사용하여 접근시 포인터 변수의 값을 주소로 보고 해당 주소에 해당하는 메모리로 접근한다는 의미이다.
위와 같은 차이로 보았을 때 포인터는 자신의 값을 주소로 보는 것이다. 하지만 참조자는 참조하는 객체의 또다른 식별자이다. 따라서 참조하는 대상이 없다면 참조자는 선언이 불가하다. 즉 r-value가 있을 때 참조자의 선언이 가능하다. 또한 참조의 대상은 상수가 될수 없다.
2. 함수에 참조자를 이용한 매개변수 전달
덩치가 큰 자료를 전달할 때 있어서 가장 효율적인 방법은 Call - by - reference 형식으로 자료를 전달하는 것이다. 예를 들어 다른 함수로 자료를 전달할 때 reference (주소) 를 통해 전달하여 reference를 이용하여 접근 한다면 메모리 사용측면에서 효율적이다.
참조자에 대해서 잠깐 정리하면 다음과 같다. 참조자는 참조 대상의 다른 별칭이라고 생각하면 된다. 예를 들어 변수 ptr_ls 를 선언후 해당 변수를 참조자 변수 list 의 참조로 초기화 할 경우 list 의 주소와 ptr_ls 의 주소는 같다. 즉 ptr_ls의 주소가 0x00000001 이라고 한다면 참조자 list의 주소도 0x00000001 이 된다. 아래 자료를 보자
int _tmain(int argc, _TCHAR* argv[]) { int cnt = 50; int* ptr_ls = new int[5]; // 메모리 동적할당 // 동적할당한 메모리의 주소를 담은 ptr_ls 포인터의 참조자인 list 변수 생성 // 이때 list 참조자가 가르키는 값은 int * 자료형으로 인식한다. // ptr_ls : 동적할당 주소 // & list : ptr_ls 참조 (list == ptr_ls) // int*(&list) : list 참조자 (현재 참조는 ptr_ls) 의 값을 해석하는 방법을 int * 로 해석 // 따라서 ptr_ls 안에 있는 주소의 메모리를 int로 해석한다. int*(& list) = ptr_ls; for (int i = 0; i < 5; ++i){ *(list + i) = cnt; cnt -= 10; } sort_chamjo(list); return 0; }
(& list) = ptr_ls : 해당 코드는 list 라는 참조자의 참조 값을 ptr_ls ( 포인터 변수 ) 로 선언 및 초기화 한 것이다.
따라서 위와 같이 list 참조자와 ptr_ls 변수는 0x 00a4fd14 라는 동일한 메모리를 가리키고 있다. 또한 동적할당 받은 메모리 주소인 0x 00d9c6d0 이라는 주소를 값으로 가지고 있다.
int* (&list) : 해당 코드는 list 참조자의 값의 해석하는 방식을 int * 즉 주소로 보겠다는 의미이다. 따라서 참조자 list가 가리키고 있는 값은 동적할당 받은 메모리의 주소이고 해당 주소로 접근하기 위해 ' int * ' 자료형으로 선언한 것이다. 아래 예시를 보면 쉽게 이해가 갈 것이다.
int &list = ptr_ls => ptr_ls 변수에 대한 참조자 list 선언 , list 참조자의 값을 int 로 해석
int* (&list) = ptr_ls => ptr_ls 변수에 대한 참조자 list 선언, list 참조자의 값을 int * 로 해석
이제 위의 설명으로 포인터 변수를 참조자의 참조 값으로 사용할 수 있는 것과 사용하는 방법에 대해서 알았을 것이다. 그렇다면 함수의 매개변수로 포인터를 전달하고 해당 함수가 받는 매개변수를 참조형으로 받아보자
void sort_chamjo(int*(&list), int len){ int tmp = 0; for (int i = 0; i < len - 1; ++i){ for (int j = i+1; j < len; ++j){ if (list[i] > list[j]){ tmp = list[i]; list[i] = list[j]; list[j] = tmp; } } } }
위와 같이 참조자 list를 선언하며 해당 참조자가 참조하는 값을 int * 로 해석하겠다고 정의하면 ptr_ls 포인터 변수가 할당 받은 동적할당 메모리의 값에 list 참조자를 사용하여 접근 할 수 도 있다.
전체 소스코드는 다음과 같다.
// HelloNew_C+.cpp : 콘솔 응용 프로그램에 대한 진입점을 정의합니다. // #include "stdafx.h" #include<iostream> #include<cstdio> #include<string> // 참조자를 매개변수로 받는 정렬 함수 void sort_chamjo(int*(&list), int len){ int tmp = 0; for (int i = 0; i < len - 1; ++i){ for (int j = i+1; j < len; ++j){ if (list[i] > list[j]){ tmp = list[i]; list[i] = list[j]; list[j] = tmp; } } } } int _tmain(int argc, _TCHAR* argv[]) { int cnt = 50; int* ptr_ls = new int[5]; // 메모리 동적할당 for (int i = 0; i < 5; ++i){ *(ptr_ls + i) = cnt; cnt -= 10; } for (int i = 0; i < 5; ++i){ std::cout << *(ptr_ls + i) << std::endl; } sort_chamjo(ptr_ls, 5); for (int i = 0; i < 5; ++i){ std::cout << *(ptr_ls + i) << std::endl; } return 0; }
결과는 다음과 같다.
'C++ 언어' 카테고리의 다른 글
함수 템플릿과 인라인 함수 (0) 2020.03.30 참조자의 내부 동작 (0) 2020.03.27 C++ 메모리 동적할당 및 해제 ( new & delete ) (0) 2020.03.26 C++ 의 변수 선언 (0) 2020.03.26 C++ ( std::cout ), ( std::cin ), (std::string) (0) 2020.03.26