참조자(Reference)의 이해
참조자는 자신이 참조하는 변수를 대신할 수 있는 또 하나의 이름이다.
예제를 통해 살펴보자.
- #include <iostream>
- using namespace std;
- int main() {
- int num1 = 1020;
- int &num2 = num1;
- num2 = 3047;
- cout << "val : " << num1 << endl;
- cout << "ref : " << num2 << endl;
- cout << "val : " << &num1 << endl;
- cout << "ref : " << &num2 << endl;
- return 0;
- }
[ 결과 ]
6행 : num1에 대한 참조자 num2를 선언하였다. 이후로는 num1과 num2가 동일한 메모리 공간을 참조하게 된다.
9~10행 : 동일한 값이 출력되면, num1과 num2가 동일한 메모리 공간을 참조함을 증명한다.
num1과 num2의 주소값을 출력하게 하였다.
참조자는 변수를 대상으로만 선언이 가능하다. 하지만 일단 선언되고 나면 변수와 차이가 없다. &연산자를 이용해서 주소값을 반환 받을 수 있고, 함수 내에서 선언된 local 참조자는 지역변수와 마찬가지로 함수를 빠져나가면 소멸된다.
즉 다음의 선언에서,
int &num2 = num1 ;
num1이 변수의 이름이라면, num2는 num1의 별칭이라는 뜻이다. 그런데 이 역시 참조자를 이해하기 위한 좋은 비유가 된다. 예를 들어 "송경식"이라는 사람의 별명이 "불곰"이라면 다음 두문장이 의미하는 바와 이 문장을 처리했을 때의 결과는 모두 동일하다.
"송경식에게 전화 해서 당장 오라고 해!"
"불곰한테 전화해서 당장 오라고 해!"
참조자의 특징
참조자의 수에는 제한이 없으며, 참조자를 대상으로도 참조자를 선언할 수 있다.
참조자는 선언과 동시에 누군가를 참조해야만 한다.
참조자는 본디 변수에 또다른 이름을 붙이는 것이기 때문에 상수를 대상으로 참조자를 선언할 수 없다.
- #include <iostream>
- using namespace std;
- int main() {
- int arr[3] = { 1,2,3 };
- int &ref1 = arr[0];
- int &ref2 = arr[1];
- int &ref3 = arr[2];
- cout << ref1 << endl;
- cout << ref2 << endl;
- cout << ref3 << endl;
- return 0;
- }
예제와 실행결과에서 보이듯이 배열 요소는 변수로 간주되어 참조자의 선언이 가능하다.
그리고 포인터 변수 또한 변수이기 때문에 참조자의 선언이 가능하다.
- #include <iostream>
- using namespace std;
- int main() {
- int num = 12;
- int *ptr = #
- int **dptr = &ptr;
- int &ref = num;
- int *(&pref) = ptr;
- int **(&dpref) = dptr;
- cout << ref << endl;
- cout << *pref << endl;
- cout << **dpref << endl;
- return 0;
- }
[ 결과 ]
10~11행 : 포인터 변수의 참조자 선언도 &연산자를 하나 더 추가하는 형태로 진행이 된다. (9행과 비교)
14행 : pref는 포인터 변수 ptr의 참조자이므로, 변수 num에 저장된 값이 출력된다.
15행 : dpref는 포인터 변수 dptr의 참조자이므로, 변수 num에 저장된 값이 출력된다.
참조자(reference)와 함수
C언어를 공부하며 배운 함수의 두가지 호출 방식은 다음과 같다.
- Call-by-value -> 값을 인자로 전달하는 함수의 호출방식
- Call-by-reference -> 주소 값을 인자로 전달하는 함수의 호출방식
- int func(int a, int b){
- return a+b;
- }
- void swapp(int a, int b){
- int tmp = a;
- a = b;
- b = tmp;
- }
위의 함수를 대상으로 다음의 main 함수를 실행해보자.
- #include <iostream>
- using namespace std;
- void swapp(int a, int b);
- int main() {
- int a = 10;
- int b = 20;
- swapp(a, b);
- cout << "a : " << a << endl;
- cout << "b : " << b << endl;
- return 0;
- }
- void swapp(int a, int b) {
- int tmp = a;
- a = b;
- b = tmp;
- }
[ 결과 ]
a와 b에 저장된 값이 서로 바뀌지 않았음을 의미한다. 그래서 필요한 것이 Call-by-reference 기반의 함수이다.
- void swapp(int *a, int *b) {
- int tmp = *a;
- *a = *b;
- *b = tmp;
- }
위의 함수에서는 두 개의 주소 값을 받아서, 그 주소 값이 참조하는 영역에 저장된 값을 직접 변경하고 있다.
따라서 위 함수를 대상으로 다음의 main함수를 실행해보자.
- #include <iostream>
- using namespace std;
- void swapp(int *a, int *b);
- int main() {
- int a = 10;
- int b = 20;
- swapp(&a, &b);
- cout << "a : " << a << endl;
- cout << "b : " << b << endl;
- return 0;
- }
- void swapp(int *a, int *b) {
- int tmp = *a;
- *a = *b;
- *b = tmp;
- }
[ 결과 ]
참조자를 이용한 Call-by-reference
Call-by-reference의 가장 큰 핵심은 함수 내에서 함수외부에 선언된 변수에 접근할 수 있다는 것이다.
참조자를 이용해서 함수를 정의해도 이러한 일이 가능하다. 다음의 함수를 살펴보자.
- void swpbyref(int &ref1, int &ref2){
- int tmp=ref1;
- ref1=ref2;
- ref2=tmp;
- }
매개 변수의 선언 위치에 참조자가 와서 햇갈릴 수 있다.
매개변수는 함수가 호출되어야 초기화가 진행되는 변수들이다. 즉 위의 매개변수 선언은 초기화가 이루어 지지 않은 것이 아니라, 함수 호출 시 전달되는 인자로 초기화를 하겠다는 의미의 선언이다. 그럼 위의 함수를 대상으로 다음과 같이 함수를 호출하면 어떻게 될까?
- #include <iostream>
- using namespace std;
- void swpbyref(int &ref1, int &ref2);
- int main() {
- int a = 10;
- int b = 20;
- swpbyref(a, b);
- cout << "a: " << a << endl;
- cout << "b: " << b << endl;
- return 0;
- }
- void swpbyref(int &ref1, int &ref2) {
- int tmp = ref1;
- ref1 = ref2;
- ref2 = tmp;
- }
[ 결과 ]
18~20행 : 두 참조자 ref1, ref2에 저장된 값의 교환 과정이다. 이 과정은 main함수에 선언된 변수 a, b의 교환으로 이루어 진다.
10행 : 매개변수로 참조자가 선언되었으니, 참조의 대상이 될 변수를 인자로 전달하면 된다.
문제
1. 참조자를 이용해서 다음 요구사항에 부합하는 함수를 각각 정의하여라.
->인자로 전달된 int형 변수의 값을 1씩 증가시키는 함수
->인자로 전달된 int형 변수의 부호를 바꾸는 함수
그리고 위의 각 함수를 호출하여 그 결과를 확인하는 main함수까지 작성하여라.
2. 다음의 코드를 살펴보자.
- int main(){
- int num1=5;
- int *ptr1=&num1;
- int num2=10;
- int *ptr2=&num2;
- }
위의 코드를 보면 ptr1과 ptr2가 각각 num1과 num2를 가리키고 있다. 이때 ptr1과 ptr2를 대상으로
swptr (ptr1, ptr2) ;
다음과 같이 함수를 호출하고 나면, ptr1과 ptr2가 가리키는 대상이 서로 바뀌도록 swptr함수를 정의해보자.
답
문제 1.
- #include <iostream>
- using namespace std;
- int pluss(int a);
- int mul(int a);
- int main() {
- int num1=10;
- int plusv, mulv;
- plusv = pluss(num1);
- mulv = mul(num1);
- cout << plusv << endl;
- cout << mulv << endl;
- return 0;
- }
- int pluss(int a) {
- int &ref = a;
- ref = ref + 1;
- return ref;
- }
- int mul(int a) {
- int &ref = a;
- ref = ref*(-1);
- return ref;
- }
문제 2.
- #include <iostream>
- using namespace std;
- void swptr(int *(&pref1), int *(&pref));
- int main() {
- int num1 = 5;
- int *ptr1 = &num1;
- int num2 = 10;
- int *ptr2 = &num2;
- cout << *ptr1 << endl;
- cout << *ptr2 << endl;
- swptr(ptr1, ptr2);
- cout << ptr1 << endl;
- cout << ptr2 << endl;
- }
- void swptr(int *(&pref1), int *(&pref2)) {
- int *ptr = pref1;
- pref1 = pref2;
- pref2 = ptr;
- }
※ 위의 답은 제가 임의로 작성한 답입니다. 여러분이 생각하고 직접 작성해보세요!!
'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 |