반응형

0. 시작하기

 

1. 포인터 ( CPP/Pointer.cpp )

참조자에 대해서 설명하기 위해서는 역시 포인터를 먼저 말하고 가야한다고 생각해서 적게되었습니다.

 

C언어를 입문하는 사람이 가장 힘들어하는 부분이 '포인터'라고 합니다.

 

포인터는 변수의 메모리상 주소를 가리키는 녀석을 말합니다.

 

조금 더 디테일하게 보겠습니다.

 

#include <iostream>
using namespace std;

int main(void){
    int a = 10;
    int *pt1 = &a;
    int **pt2 = &pt1;

    cout << a << endl;      // a의 값
    cout << &a << endl;     // a의 주소값

    cout << "\n---------------\n" << endl;

    cout << pt1 << endl;    // pt1의 값
    cout << *pt1 << endl;   // pt1이 가리키는 값
    cout << &pt1 << endl;   // pt1의 주소값

    cout << "\n---------------\n" << endl;

    cout << pt2 << endl;    // pt2의 값
    cout << *pt2 << endl;   // pt2가 가리키는 값
    cout << &pt2 << endl;   // pt2의 주소값

    return 0;
}

 

위와같은 코드를 하나 작성해보았습니다. 그리고 실행해보면 아래와 같은 결과를 볼 수 있습니다.

 

실행결과

 

 

이를 그림으로 표현하면 아래와 같습니다.

 

메모리상 데이터와 주소

어려워 보이는 개념일 수 있습니다만, 간단하게 생각해봅시다.

 

1개의 포인터(*)는 변수의 주소를 직접 갖고 있다는 의미입니다.

 

2개의 포인터(*)(=더블포인터)는 변수의 주소를 갖고있는 포인터의 주소를 갖고있다는 것을 말합니다.

 

포인터의 갯수가 3개, 4개가 되도 마찬가지입니다.(물론 3,4개씩 쓸일이 있을까 싶기는 합니다.)

 

C언어 기준으로 본다면, 포인터는 아래와 같습니다.

 

  • int *pt : 변수의 주소값을 저장할 'pt'라는 포인터를 만듭니다.
  • int **ptt : 변수의 주소값을 갖고있는 포인터의 주소값을 가리킬 (더블)포인터를 만듭니다.
  • print(*pt) : 포인터 pt가 가리키는 값을 출력합니다.
  • print(&pt) : 포인터 pt의 주소값을 출력합니다.

위의 4개만 알고있다면 포인터를 쉽게 사용은 못해도, 포인터를 이용한 다양한 알고리즘, 자료구조들을 이해하는데는 충분합니다.

 

 

2. 참조자 ( CPP/Reference1.cpp ~ Reference3.cpp )

그럼 본론으로 돌아가서 C++의 참조자에 대해서 언급해보겠습니다.

 

위에서 보았듯이 C++에서도 C언어와 마찬가지로 포인터(*)를 사용할 수 있는 것을 볼 수 있었습니다. 하지만, 프로그래밍을 할 때, 포인터를 사용한다는 것은 익숙하지 않은 프로그래머에게는 상당히 부담스러울 수 있는 일입니다.(난이도 때문)

 

이러한 문제 때문이었는지 C++에서는 '참조자'라는 특별한 친구를 만들어 주었습니다.

 

참조자는 변수에 대한 별명을 만들어주는 것이라 볼 수 있습니다.

 

예제 코드 몇개를 살펴 보겠습니다.

 

#include <iostream>   // CPP/Reference1.cpp
using namespace std;

int main(void){
    int x = 10;
    int &y = x;  // int y = x 사용시 결과는 11, 11

    x++;
    y++;

    cout << x << endl;
    cout << y << endl;

    return 0;
}

실행결과

 

만약 [ int y = x ]라고 해주었다면, 결과는 11, 11이 출력되었을 것입니다.

 

왜냐하면 변수라는 것은 [ 데이터 ]를 저장하는 공간이기 때문입니다. 데이터, 즉 값이라는 것을 저장하는 변수는 특정한 숫자나 특정한 문자열을 저장할 뿐이지 다른 변수의 주소값등을 저장하지는 못합니다.

 

따라서 위의 코드에서 [ int &y = x ]라는 부분이 특별한 기능을 한다는 것을 알 수 있습니다.

 

변수 선언과정에서 변수명 앞에 '&' 를 붙일 경우, 이것은 변수가 아닌 '참조자'라는 것으로 선언되었다는 의미를 갖습니다.

 

참조자는 위에서 언급한것과 같이 '변수'에 대한 '별명'을 붙여준다는 것을 말합니다.

 

위의 예제를 조금 수정해서 아래와 같이 실행해 보았습니다.

 

#include <iostream>  // CPP/Reference2.cpp

using namespace std;

int main(void){
    int x = 10;
    int &y = x;
    int &z = y;

    cout << &x << endl;
    cout << &y << endl;
    cout << &z << endl;

    return 0;
}

실행결과

위의 코드를 보게되면 '변수x'와 '참조자y'는 동일한 주소값을 갖고있는 것을 볼 수 있습니다.

 

그림으로 그려본다면 아래와 같은 모습임을 예측할 수 있습니다.

 

 

중요한것은 [변수x]와 [참조자 y, z]가 존재하는 형태라는 것입니다.

 

여러개의 변수가 하나의 데이터 공간을 공유하는 것은 아니라는 것을 꼭! 기억했으면 좋겠습니다.

( y 나 z가 없는 x는 존재할 수 있지만, x가 없는 y 또는 z 는 존재할 수 없습니다.)

 

 

참조자가 어떤 것인지는 대충 아셨을 것이라고 생각합니다. 그럼 이제 참조자가 어떤식으로 사용되는지 알아보겠습니다.

 

참조자의 가장 큰 특징은 딱 두가지입니다.

 

  • 참조자는 선언과 함께 참조할 변수가 정해져야 합니다. ( int &x; 와 같이 선언 불가)
  • 참조자는 함수 호출시 매개변수로 갖을 수 있다.

제가 설명력이 부족해서 이해하기 힘드실테니, 예제코드를 통해서 알아보겠습니다.

 

#include <iostream>

using namespace std;

void swap(int &a, int &b);

int main(void){
    int x = 10;
    int y = 20;

    swap(x,y);

    cout << x << endl;
    cout << y << endl;

    return 0;
}


void swap(int &a, int &b){
    int temp = a;
    a = b;
    b = temp;
}

실행결과

 

일반적으로 swap 함수를 사용할 경우 포인터를 사용해주었었습니다. 왜냐하면 함수의 경우 일반적으로 [Call-By-Value]를 원칙으로 하기 때문입니다.

( Call-By-Value, Call-By-Reference 에 대한 내용은 다음페이지에서 다루겠습니다. )

 

위의 swap 코드를 보게되면 [ int &a, int &b ]의 형태로 '참조자'를 이용한 것을 볼 수 있습니다.

 

따라서 이 swap 함수에서의 a, b라는 값은 메인함수의 [ x, y ]라는 변수의 '별명'이 되는 것을 볼 수 있습니다.

 

여기서 혹시 [ int &a ] 형태로 참조자는 선언할 수 없냐고 했는데 사용가능한건가? 하는 의문이 있을 수 있습니다.

 

결과는 당연히 가능합니다. 왜냐하면 함수의 매개변수는 함수가 코드내에서 실행되는 순간 할당되기 때문입니다.

 

위의 함수를 '인라인' 된것처럼 본다면 다음과 같은 코드가 될 것입니다.

 

#include <iostream>

using namespace std;

int main(void){
    int x = 10;
    int y = 20;

    // Swap 함수 영역 시작
    swap(int &a = x, int &b = y){
    	int temp = a;
        a = b;
        b = temp
    }
    // Swap 함수 영역 끝

    cout << x << endl;
    cout << y << endl;

    return 0;
}

'실제 코드가 아닌 추상적인 코드입니다'

 

 

위와같이 함수는 실행되는 과정에서 각각의 매개변수에 값을 할당하기 때문에 참조자만을 선언하는 것이 아니라는 것을 알 수 있습니다.

 

참조자는 정말 중요한 개념이고 다양한 응용이 있기 때문에 다음 페이지에서 조금 더 알아보도록 하겠습니다.

반응형

+ Recent posts