본문 바로가기
2_ 바삭바삭 프로그래밍/C and C++

C++ - 쉽게 설명한 포인터(point)와 레퍼런스(reference) 이야기

by 준환이형님_ 2012. 5. 22.


포인터를 배우다보면 누구나 몇 가지 알쏭달쏭한 점을 느끼게 되지요(point로 쓰고 포인터라고 읽는 점 부터가 이상해..-_-)


모든 코딩이 그렇지만 포인터는 특히 직접 컴파일을 하면서 이해하는 것이 가장 좋은 방법이 아닐까요. 그치만.. 나도 게을러서 이해하고 땡하는게 더 좋아요 :)


오랜만의 포스팅으로 C의 꽃, 포인터에 관한 내용을 정리 해두고 싶었답니다. 



우선.. 포인트, 레퍼런스 사용의 장점에 대해 잠깐 생각 해 봅니다.


1. 직접적인 접근이 가능하다는 것에 있겠죠. 포인터를 이용한 주소 접근은 위험할 수 있지만 그만큼 강력한 컨트롤을 할 수 있습니다.

   물건 깎을 때, 점원을 부르는 것 보다 주인을 부르는 편이 낫겠죠_


2. 효율적이구요. 바탕화면 위에 파일을 끌어다 두는 것보다 [바로가기]들로만 만들어 두는 것이 더욱 CPU점유율을 낮출 수 있는 것과 같은 원리.


3. 편리하고 쉬운코드를 짤 수 있어요. 단, 사용시 이해도가 높은 것이 좋아요.


c언어가 어렵지 않기 때문에 고급언어라고 부르지만 또한, 강력한 주소접근이 가능하기 때문에 중간언어라고도 하는 이유를 여기서 찾을 수 있지요 



뚜뚜뚜- nuclear launch detected-!



포인터와 레퍼런스를 배우기에 앞서 우선 우리가 아래의 두 가지 사항을 염두해 두고 시작한다면 이해에 많은 도움이 됩니다.


1. 선언 할 때 / 선언이후 사용할 때가 서로 달라서 혼동이 있을 수 있지요. 


그리고 


2. 포인터는 초기화 할 때 주소를 넣어주고,

   레퍼런스는 값의 형태(변수든 포인터변수든 cout 시키면 값이 출력되는 상태)를 넣어주고 시작해야 합니다.


우선


int *p; 


라고 포인터를 선언하게 됩니다. 얘는 선언 이후부터는 


p; 


주소값이 됩니다. ( cout<<p; 해보면 알 수 있죠) 그리고 *p는 실값(속에 저장되어 있는 내용)이 출력 됩니다.


레퍼런스는


int a = 30; (레퍼런스 당할 변수 - 신용카드의 돈이 빠져나갈 기반이 되는 예금통장이라고 할까요)


int &rep = a; 으로 선언을 합니다. (초기화에서 연결 시켜주지 않으면 에러가 나지요)


우리가 일반적으로 변수를 선언(inr a;)한다는 것은 1. 해당주소할당(0X$#%^)과 2. 이름표(a)가 하나씩 만들어지는 것이라고 볼 수 있습니다.


레퍼런스 변수는 해당 주소에 이름표를 두개( a랑 rep) 만들어 주었다고 할 수 있지요.


그래서 레퍼런스 선언이후 부터 이 아이는 똑같은 일반 변수처럼 쓸 수가 있어요.


cout<<rep 이라고 하면 a 값이 바로 출력이 되지요.


cout<<&rep; (주소)을 출력하는 것은 cout<<&a; 를 출력하는 것과 완전 같은 말이 됩니다.



이렇게 이론으로 이해를 해도 막상 함수호출 할 때는 느낌이 또 다르죠 ^^


func(int &c, int &d)

{

}


void main()

{

int a, b;

func(a,b);

}


이 말은  int &c=a; 라는 말이므로 함수에서 값을 변경하더라도 실제 값 변환(call of reference)이 가능합니다. 


예를 하나 들어,


void func(int &d)

{

}


void main()

{

int a=30;

int *c=&a;

func(*c);            // int &d (선언) *c (선언이후이므로 *는 포인터변수의 실값을 의미);

}


이것도 void func(int &d)가 주소를 받아 줄테니 메인에서 보내줄 때도 *을 찍어서 주소로 보내주어야 하겠네요. 

아까 당부사항 2번 - 레퍼런스는 값의 형태(변수든 포인터변수든 cout 시키면 값이 출력되는 상태)을 넣어주고 시작하기 때문입니다.


반대로 한 번 해볼까요.


void func(int *d)        // 포인터변수의 생성을 의미하므로 *을 찍어주어 주소를 받습니다.

{

cout<<*d;        // 이 d는 선언때의 d가 아니므로 *을 찍어주면 실값이 출력이 됩니다.

}


void main()

{

int a=30;

func(&a);

}


우리가 흔히 말하는 다중 포인터에도 이슈가 많이 있지만, 

기본적으로는 포인터들을 포인터로 연결하기 위해 이용된다고 볼 수 있습니다.


1. 하나의 포인터를 연결할때


int a=30;

int *pa=&a;

int *ppa = pa;    // *ppa는 선언이므로 *로 시작, pa는 윗줄에서 선언했으므로 *없이 연결


cout<<*ppa;


단순히 이렇게 이어줄수 있죠. 그리고 이것도 가능해요


2. 하나의 포인터를 다중포인터로 연결


int a=30;

int *pa=&a;

int **ppa = &pa;


cout<<**ppa;    //이중포인터이므로 &ppa는 자신의 주소, *없이는 자신이 가리키는 주소(&pa - pa의 주소),

 *하나를 넣으면 pa가 가리키는 아이의 주소(&a)를 출력, *두개를 넣으면 pa가 가리키는 a의 값


그러나 굳이 다중포인트를 쓰지 않아도 되므로 의미가 크다고는 할 수 없겠죠..


다차원의 배열에 접근을 하기 위해서 배열마다 하나씩 주소를 연결해 쉽게 접근을 할 수 있습니다.


다차원의 배열에 접근에 관한 다중 포인터와 그 밖의 활용에 대해서는 다음시간에 알아보도록 하겠습니다.


변수나 클래스의 동적할당, 할당된 클래스 변수에서 메소드의 호출 등으로 가기 위해서 포인트는 확실히 아는 것이 좋습니다.


저는 동적 할당 된 라이브러리 클래스나 MFC를 처음 접하게 되었을때 포인트나 레퍼런스에 대한 이해가 부족하여 답답했던 경험이 자주 있었답니다. 


건드리기만 하면 에러가 나서 뭘 할 수가 없었죠.