Programming/C++

sbshope 2008. 9. 8. 08:40

참조와 포인터의 내부적인 차이

기존 c언어에는 메모리 안의 기억공간을 나타내는 방법으로

 

값에 의한 전달 방식과 주소에 의한 전달 방식이 있다

 

c++언어에서는 여기에 참조에 의한 전달방식(call by refenrence)가 추가 되었다

 

함수의 인자로 사용될때 값에 의한 전달방식은 변수의 값을 복사하여 사용하고 함수가 닫히면

 

값은 사라진다 

 

주소에 의한 전달 방식은 포인터가 가르키는 곳의 값을 바꿀경우 그값은 함수가 닫혀도

 

바뀌지 않고 고정된다. 하지만 인자로 넘어온 주소를 바꿀경우 그 주소는 함수가 종료될때 

 

사라진다

 

참조에 의한 전달방식 역시 포인터와같이 주소를 가지는 참조변수를 가진다

 

주소가 가르키는 곳의 값을 직접 바꾸기 때문에 함수가 닫혀도 변한 값은 변하지 않는다

 

포인터변수와 참조변수는 비슷하지만 차이점이 존재한다

 

1

포인터 변수는 주소를 가지고 언제든지 주소를 다시 바꿀수가 있습니다

int  b , c;

int  *a;

a = &b; 

a = &c;

참조 변수는 초기화 할때 설정한 변수의 주소를 이후에 변경할수 없습니다

int b , c ;

int &a =b;

a = c ; // 변경할수 없다

 

2

위 예제 처럼 포인터 변수는 &를 나타내서 주소를 설정하는데 반해

참조 변수는 b 와 같이 변수명을 그대로 사용해서 주소를 설정한다

 

3

포인터는 *a 형태에서만 값을 조작할수 있지만 참조는 변수명만으로도 직접

값을 조작할수 있다

 

4

포인터는 주소와 값을 조작할수있지만

참조는 값만을 조작 할수 있다

참조변수에 주소를 설정하는것은 초기화때만 할수 있기 때문이다

 

연습

 

#include <iostream.h>
#include <stdlib.h>

void test(int *x,int*y); //주소에 의한 전달 방식
void test(int &x,int&y); //참조에 의한 전달 방식
void value_test(int x, int y); //값에 의한 전달 방식

void main(void)
{
 int a = 5;
 int b = 3;

 test(&a,&b);
 cout << " a = " << a << " b = " << b <<endl;
 test(a,b); 
 cout << " a = " << a << " b = " << b <<endl;
 value_test(a,b); 
 cout << " a = " << a << " b = " << b <<endl;

}
void test(int *x,int*y)
{
 *x = 3;
 *y = 5;
}
void test(int &x,int&y)
{
 int tmep = x;
 x = y;
 y = tmep;
}
void value_test(int x ,int y)
{
 int  temp =x;
 x = y;
 y = temp;
}

 

 

 

결과

 

========================================================================== 

우선 차이점부터

 

포인터는 그 포인터가 가리키는 대상을 변경 가능합니다.

                   그 대상의 물리적 위치 정보(시작 주소)를 저장하고 있으니까....

 

레퍼런스는 가리키는 대상을 변경할 수 없습니다.

                    레퍼런스의 대상 변수가 어느것인가가 정해지는 것은 레퍼런스가

                    초기화(정의와 동시에 초기화)될 때 뿐이죠.

 

하지만 레퍼런스는 대상을 변경할 수 없는 대신에 역참조 연산자를 안붙이고

                   원래의 변수처럼 사용할 수 있습니다. 기능이 제한된 아주 특수한 형태의

                    포인터라고 해도 됩니다.

                    (이게 레퍼런스의 용도라고 할 수 있죠. 사실상 참조에 의한 호출을 위해서만

                    사용합니다.)

 

C++에서 레퍼런스가 생겼어도 포인터는 여전히 그 유용성을 갖고 있죠.

                  어느 한 변수 이름이 가리키는 대상을 제어의 조건에 따라 간단히 변경할 수

                  있다는 것은 대단히 매력적인 일입니다. 또한 배열을 넘겨줄 때도

                  포인터가 유용합니다.

                  (참조에  의한 호출 용도는 레퍼런스에게 뺏겼습니다. 포인터는 역참조연산자가

                   필요해서 불편하거든요.)

 

첫 번째 코드 질문 : 실제 변수(메모리공간)가 하나이므로 한 번만 delete 해주면 됩니다.

                           하지만 그 이후에는 b를 쓰면 안되죠. 허가 안난 메모리를 사용하면

                           실행중에 메모리 오류가 생길 수 있습니다. 초기화 안 된 포인터에 아무

                           값이나 넣고 *를 붙여서 사용하는 것처럼....

 

두 번째 코드 질문 : 뭘 원하시는 것인지 잘 모르겠지만 제가 이해한 대로 설명하죠.

                           main 함수의 지역변수인 포인터 변수 str, ststr을 호출되는 함수의

                           str, ststr과 똑 같은 메모리 공간이 되게 할 수 있느냐는 거죠? 레퍼런스를

                           사용해서?

                           가능은 하지만 쓸모가 없답니다. 적어도 위의 함수의 경우는 그렇게 할

                           필요가 없습니다. str과 ststr은 실제의 문자열 정보를 담지 않습니다.

                           단지 문자열이 있는 장소를 알고있을 뿐이죠. 변경대상은 str, ststr 자체

                           가 아니라 가리키는 문자열이기 때문에 문자열이 어디있는지만 알려주는

                           지금의 코드로 충분하답니다.(C/C++에서 문자열과 배열은 동등하죠. 배열을

                           넘겨줄 경우도 같은 이유로 레퍼런스를 안씁니다.)

 

                           그럼 레퍼런스를 쓰면?? 위의 코드를 레퍼런스를 사용해 호출로 호출할

                           경우는 str 이 가리키는 문자열과 ststr이 가리키는 문자열을 송두리째 바꿀

                           목적이겠죠. 다음의 코드를 보십시오.

 

void char swap( char *&stra, char *&strb )   // 문자 포인터에 대한 참조자 두 개

{

           char *tmp;

                

           tmp = stra;                             

           stra = strb;

           strb = tmp;

}          //  두 문자열(정확히는 포인터)서로 바꾸어주는 코드입니다.  문자열 복사 없이...

// 이 코드는 문자열을 메모리 째로 바꾸어주지만 각 메모리 내의 내용은 변함없습니다.

// 포인터의 개념을 더 잘 생각해 보시기 바람...  여기에 배열 이름을 대입해서 호출하면

//  안됩니다....

========================================================================================= 

예를들면: (____ <- tab 으로 사용됩)

int main(void)
{
____int *x;
____int z = 0;

____x = &z; /* 첫번째 */
____printf("%d", *x);

____x = z; /* 두번째 */
____printf("%d", x);
}

첫번째:

포인터는 쉽게 말하자면, 손가락이죠. 그 손가락을 어디로 포인트 하고 있냐하면, 바로 메모리 블록 입니다. & 참조는 C 에서 메모리 블록의 주소를 가져다 주는 역활을 합니다. 그럼 이제;

x = &z; 는 포인터 x에게 z의 주소를 가져다 주네요. 그럼으로서 포인터는 메모리 주소로 손가락질을 하고요, 그 다음 문장 printf 에선, 그 손가락질을 하고 있는 곳에 숫자를 출력하죠.

두번째:

x = z; 는 손가락질을 메모리 블록이 아닌 숫자에게 하다보니, 불가능한 문장이 되는거죠.

 

==================

 

많은 사람들이 참조자와 포인터를 완전히 다른것이라고 생각을 하는데요..

사실 이 둘은 다르지 않습니다..

일반적으로 c 언어를 배운후 c++을 배운사람들은 포인터와 참조자가 연관성이

있다는것을 눈치채게 됩니다..

하지만 c++을 먼저 시작한 사람들은 포인터와 참조자가 헷갈리기만 하죠..

참조자는 한마디로 포인터를 캡슐화시킨것입니다..

겉으로는 일반변수처럼 보이도록 캡슐화시켰지만 사실 내부적으로는 포인터와 동일한 처리를

하는것이지요..

이렇게 캡슐화되었기 때문에 우리들은 참조자에게서 포인터의 모든기능을

사용할 수 없습니다..

참조자의 기능은 아래와같이 제한됩니다..

 

1. C++의 주소연산자를 이용하여 참조의 주소를 구할수 없다.

2. 참조에 포인터를 할당할 수 없다.

3. 오프셋 값을 더하는것과 같은 수치 연산을 할 수 없다.

4. 참조를 변경할 수 없다..

 

 

* 참고로 참조를 많이 쓰면 프로그램 읽기 힘들어여..

 

======================

 

우선 용어부터 햇갈리시는 것같네요

함수 호출시 parameter 전달 방법으로는

call by referance(참조호출, address에 의한호출) 과
call by referance(값 호출)
가 있으며

c 에서는 기본적으로 참조호출을 지원하지 않습니다
다만 포인터를 사용하여 참조호출의 효과를 얻을 수 있을 뿐이니다.

간단한 예를 들어 두값을 바꾸는 함수 swapValue(a,b)라는 함수가 있을때

call swapValue(a,b) 를 수행하고 나서
호출측의 a,b값이 서로 바뀌었다면 call by referance이고
함수내에서만 바뀌고 호출측의 a,b값이 안 바뀌면 call by value입니다

어떻게 이런 두 경우가 생기냐 하면 함수 호출시
파라메터 a,b의 값(a,b의 복사본)을 전달하면 함수내에서
전달 받은 a,b로 아무리 별 짓 다해도 호출측의 a,b(원본)는 변화가 없고
파라메터 a,b의 address(a,b의 위치를 가리키는 주소)를 전달하면
함수내에서 해당 위치의 값을 변경시킨다는 것은 호출측의 a,b를 변경시키
는 것과 동일한 작업이 됩니다.

c의 coding예를 보면

void swapValue_byVal(int a, int b) //call by value
{
int c;
c=a; a=b; b=c; //호출측의 a,b와 함수내의 a,b는 다른 영역
}

void swapValue_byRef(int *a, int *b) //call by referance "효과"
{
int c;
c=*a; *a=*b; *b=c; //전달 받은 주소로 작업, 위함수와 coding이 다르다
}

void main()
{
int a,b;

a=1; b=2;
swapValue_byVal(a,b); //a,b에 해당하는 1과2를 전달
//함수 수행후에도 a,b는 변화가 없다
swapValue_byRef(&a,&b); //a와b의 주소를 전달, 호출 방법이 위와 다르다
//수행후 a,b의 값이 바뀌어진다
}

c에 call by referance가 없다고 한 이유는 포인터 전달시도
a,b위치(포인터)값의 복사본이 파라메터로 전달 되기 때문입니다.
따라서 call by referance효과를 얻으려면 함수호출시나 함수내에서나
coding이 달라지게 됩니다.

사용 구분 방법은 위에서 예를 보였듯이 호출측에서 함수로 전달하는 변수의 값이
함수 수행후 바뀌기 바라면 pointer를 사용하여 참조호출의 효과를
얻어야 할것이고,
호출측의 변수변화없이 값만 전달하는 경우에는 call by value를 사용합니다

참고로 call by reference에서는 상수(constant)를 paremeter로 사용할 수

없습니다. (조금만 생각해 보면 당연한 말이란걸 알 수 있을겁니다)

 

 

 

 

명쾌한 설명 감사합니다!