2013. 4. 29. 16:46
다음의 내용은 Rushmo에서 포인터 교육 자료로 만든 내용입니다.
가져 가실때는 이 정보 남겨 주시는것이 예의 겠지요 ^^
아래 코드를 보자. ( 이 코드만 이해하면 포인터는 60% 이해 한 것임 ^^ )
char * p;
p = new char[10];
for (int i=0; i<10; i++)
p[i] = i;
printf("p address=%x \r\n",p); // 할당된 메모리가 위치하는 주소값
printf("&p[0] address=%x \r\n",&p[0]);
printf("&p address=%x \r\n",&p); // char * p 가 할당된 주소값
printf("p[0] data=%d \r\n",p[0]);
printf("*p data=%d \r\n",*p); // p 에 있는 값을 조회
printf("*(&p) data=%x \r\n",*(&p));
delete p;
위 결과 값이 아래와 같다. ( 컴파일 할 때마다 다르지만 내 경우는 아래와 같았음. )
p address=397f18 <- (1)
&p[0] address=397f18
&p address=12fe58 <- (2)
p[0] data=0
*p data=0
*(&p) data=397f18
위에서 혼돈스러운 부분이 (1) p 의 주소와 (2) &p의 주소의 의미이다.
(1) (1) 은 할당된 10바이트의 메모리가 시작되는 위치를 가리키는(point하는) 것
(2) (2) 는 그 포인터(변수 p)가 위치한 곳의 주소를 말하는 것이다.
그림으로 그리면 아래와 같다.

위 도표를 보면 알 수 있듯이
&p 는 p 변수가 위치한 주소를 가리키며
디버거로 그 주소값에 있는 데이터를 보면 메모리를 할당한 곳의 첫번째 주소를 가리키는 것이 확인 가능하다.
l p = new char[10];
char형식의 10개 배열을 만들고, 그 데이터가 시작되는 메모리를 리턴해서 p에 저장하라는 의미
l ☆ *p 는 해당 포인터에 들어 있는 값을 조회 하는 형식의 기호로 사용된다.
l 실제로는 x86 계열이 리틀엔디언을 사용하기 때문에 18 7f 39 00 형식의 값이 들어 있음.
l new, malloc, HeapAlloc 등의 함수는 heap 공간에 메모리를 할당하고 그 할당된 주소를 리턴한다. ( new는 생성자를 호출하고 , malloc 계열은 메모리만 할당 함 )
l C 언어에서는 배열형 변수타입이 없다.
[ ] 을 이용해 배열을 선언하고 배열은 결국 연속된 메모리 공간을 의미하기 때문에 , 배열의 첫번째 주소는 포인터가 가리키는 주소와 동일하게 된다.
다음은 많이 사용하는 포인터 연산을 보겠다.
아래 코드를 보자.
char * p;
p = new char[10];
for (int i=0; i<10; i++)
p[i] = i;
for (int i=0; i<10; i++)
{
printf("p[%d] data=%d \r\n",i,*(p++) ); (1)
}
delete p; // 오류 발생 (2)
p++ 을 통해서 포인터가 이동을 하고 그 이동된 위치의 값을 출력한다.
다만 이렇게 했을 때 p 를 삭제하게 되면 잘못된 곳의 메모리를 지워 버리는 문제가 발생한다.
l (1) 문장은 아래와 동일한 결과를 출력한다.
printf("p[%d] data=%d \r\n",i, p[i] ); 또는
printf("p[%d] data=%d \r\n",i,*(p+i) );
단 이렇게 했을 때 (2) 에서 발생하는 오류는 발생되지 않는다.
l 포인터에서 증가 연산자 수식
수식
|
의미
|
v = *p++
|
p가 가리키는 값을 v에 대입한 후에 p주소를 증가한다.
|
v = (*p)++
|
p가 가리키는 값을 v에 대입한 후에 가리키는 값을 증가한다.
|
v = *++p
|
p를 증가시킨 후에 p가 가리키는 값을 v에 대입한다.
|
v = ++*p
|
p가 가리키는 값을 가져온 후에 그 값을 증가하여 v에 대입.
|
다음은 Double Pointer 내용을 보겠다.
주소값의(포인터) 값을 저장하는 역할을 한다.
Int row = 3
,col = 7
,inc = 0;
char * * dp;
dp = (char **)new char[sizeof(char*) * row]; (1)
for (int i=0; i<row; i++)
dp[i]=new char[col]; (2)
for (int i=0; i<row; i++)
for (int j=0; j<col; j++)
{
dp[i][j] = inc++;
}
for (int i=0; i<row; i++)
for (int j=0; j<col; j++)
{
printf("dp[%d][%d] data=%d \r\n",i,j,dp[i][j] );
}
for (int i=0; i<row; i++)
delete dp[i]; (3)
delete dp; (4)
위의 코드는 동적인 2차원 배열을 만들고 데이터를 넣는 일반적인 소스이다.
위 코드의 메모리 구조는 아래와 같다.
![]() |
l dp = (char **)new char[sizeof(char*) * row];
row 수만큼 4 바이트의( sizeof(char*) ) 주소값을 저장할 공간을 확보해 dp에 리턴
l 생성된 메모리를 삭제 할 때는 바로 (4)를 호출 하지 않는다.
이렇게 하면 (1)에서 생상된 주소값을 저장할 공간을 삭제할 수 있으나 (2)에서 생상된 메모리를 삭제하지 않기 때문에 memory leak 발생하고 반복적일 때는 시스템 장애를 초래한다.
l 개인적으로는 공통작업에서 Double Pointer 등과 같이 분석하기 어렵게 하는 코드는 사용하지 않았으면 좋겠음 ^^.
l 위 소스 코드에서 dp[0], dp[1], dp[2] 의 데이터는 무엇을 의미할지 생각해 보시길.
댓글 없음:
댓글 쓰기