대학교에 다니면서 잠시나마 적었던 C언어 기초 강좌 입니다.

그나마 Pointer는 정성들여 썼길래 이 blog에 옮겨서 적어 놓습니다.

14강 2부 포인터의 개념

그럼 본격적으로 포인터에 대해서 알아 보도록 하자. 우선 C언어 책에 나와있는 정의를 보고 넘어가자.

포인터란? 값자체가 아니라 그 값이 있는 곳의 주소가 저장되어 있는 변수

역시 어렵다 ㅡㅡ; 일반적으로 우리가 지금까지 사용해온 변수는 어떠한 데이터값 즉 작은수 35, 큰수 90451234, 소수점 0.0432 혹은

문자 ‘A’ 와 같은 데이터 값을 저장하는 용도로 써왔다.

그런데 포인터란 어떤 특정한 데이터값이 아닌 주소를 저장하는 변수의 형을 뜻한다.

지난 14강 1부 시간에 우리가 변수의 주소에 대해서 배운 것을 기억 할 것이다. 즉 포인터는

0x0012FF7C와 같은 주소값을 저장한다. 일반적인 포인터 변수의 선언은 아래와 같은형식으로 한다.

int *pA1;

참고적으로, 일반적으로 다른 일반 변수와 구분하기 위해 포인터 변수일경우 이름앞에 p를 붙인다.

int형 변수를 선언하는 것과 똑같은데 다른점은 변수 pA1앞에 * 기호가 있다는 것이다.

*는 포인터 연산자라고 부르며 변수선언시에 *를 앞에 붙여주면 이 변수를 포인터 변수로 선언한다는 의미다. int에 *를 붙이듯

다른 변수형에 모두 *를 붙이면 포인터 변수가 된다.

double *pD; long *pL;

이렇게 선언된 포인터 변수에 주소값을 집어 넣어보자.

int a1 = 3; 
int *pA; 
pA = &a1;

우선 int a1=3; 에서 a1이라는 int형 변수를 선언후 a1에 3을 저장한다. 그리고 pA라는 포인터 변수를 하나 선언했다.

다음 pA에 &a1을 대입한다. 여기서 &a1은 a1변수의 주소값인 0x0012FF7C가 되는 것이고, 이 주소값을 pA에 저장하게 된다. 실제 pA의

값을 출력하면 0x0012FF7C가 된다.

int a1 = 3; int *pA; pA = &a1; int a2; a2 = *pA;

위 예제에 2개의 문장이 추가가 되었다. int형 변수 a2를 선언하고 a2에 *pA를 대입하고 있다.

*(포인터)의 의미는 변수를 선언할때는 포인터 변수선언이라는 뜻이지만 그외에는 pA가 저장하고 있는 주소가 가리키는 데이터 값을 뜻

한다. 현재 pA는 0x0012FF7C라는 주소값 즉 변수 a1의 주소값을 가지고 있다. 그렇다면 *pA는 0x0012FF7C 주소번지에 저장된 데이터

값, 즉 3 이라는 값을 뜻하게 된다. 그리하여 a2에는 3이라는 값이 저장되게 된다.

지금 까지 설명 한것을 직접 눈으로 보도록 하자.

c_14_2_1

현재 int a1=3; int *pA; 까지의 상태는 a1의 값이 3, &a1(a1의주소값)이 0x0012FF7C이다.

pA의 데이터 값은 일반변수와 마찬가지로 현재 선언만 되어 있는 상태이기에 0xCCCCCCCC라는 값이 저장되어 있다.

c_14_2_2

pA = &a1; 을 실행후 pA의 값에 a1의 주소값인 0x0012FF7C라는 값이 저장된 것을 볼수 있다.

c_14_2_3

a2 = *pA; 후에 a2에 데이터값 3이 저장되는 것을 볼수 있다. 이는 단순한 3이라는 값을 a2변수의 저장공간에 복사만 할뿐 a2와 pA간

에 주소값은 서로 다른것을 볼수 있다.

pA = 0x0012FF7C, &a2 = 0x0012FF74

지금까지 가장 기본적인 포인터를 사용해 보면서, int형 포인터 변수를 이용해 보았다. 다른 포인터형 변수라고 다를것은 없다. int형이

든, double형이든, char형이든 모든 포인터 변수는 단지 주소만을 저장 할 뿐이다. 그렇다면 이 포인터형의 크기는 결국 0x0012FF7C라

는 주소만을 저장하면 되기 때문에 그 종류에 상관없이 크기가 4Byte면 충분하다. 여기서 포인터형변수가 왜 4byte인지 의문점이

해결된다.

다음으로 포인터를 사용해서 배열을 다루는 것에 대해 알아보자.

배열은 이미 예전 강좌에서 설명을 했었다. 필요한 부분만 다시한번 짚고 넘어가면,

int a[3];

int형 배열 a를 선언하는 선언문이며, 여기서 배열의 이름인 a라는 것은 a배열의 시작주소값을 뜻한다고 했었다. 시작주소값이라고 하면

0x0012FF74같은 주소를 의미하는 것인데… 그럼 이것을 포인터 변수에 넣을 수 있지 않을까?

한번 시도해 보자.

int a[3]; 
a[0] = 1;
a[1] = 2;
a[2] = 3;
int *pA; 
pA = a;

c_14_2_4

int a[3]; 배열 선언후 각각 1,2,3이라는 값을 삽입했다. 배열의 이름인 a의 값을 출력해보니 역시 우리가 공부했던대로 배열의 시작주소

값인 0x0012FF74라는 값을 보여주고 있다.

c_14_2_5

우리의 예상대로 포인터 변수 pA에 a배열의 시작주소값인 0x0012FF74가 저장되는 것을 눈으로 확인할 수 있다.

그렇다면 여기서 *pA의 값을 출력하면 어떻게 될까? *pA는 pA가 가지고 있는 주소값이 가리키는 데이터 값이라 설명했다. 그렇다면 현

재 pA는 a의 배열의 시작주소값이므로 a[0]의 주소값(&a[0])을 가지고 있다고 할수 있다. 즉 *pA는 a[0]의 값인 1이 출력된다.

c_14_2_6

그렇다면 포인터 변수를 이용해서 a배열의 시작인 a[0]말고 a[1]이나 a[2]에 접근 할수 있을까?

방법은 아래 방식을 이용한다.

pA + 1

포인터 변수 pA + 1은 pA가 가리키고 있는 주소값에 포인터 변수의 형(int)의 크기만큼 더하게 된다. 즉 현재 pA가 0x0012FF74라고 하면

+1은 int형이 4byte이기 때문에 pA+1은 0x0012FF78의 주소값을 가지게 된다. 연속적으로 pA+2는 4byte*2 인 8btyte를 더한 0x0012FF7C

가 되는 것이다.

기존에 배열에 관해 배웠을때 배열에 할당된 메모리는 연속적으로 위치해 있다고 했다. 그렇다면 a[0]다음 4byte가 a[1]이듯 현재 pA

가 a[0]의 주소값을 가지고 있다면 pA+1은 a[1]의 주소를 가지게되고 pA+2은 a[2]의 주소를 가지게 된다.

실제 결과를 보자.

c_14_2_7

아마 많은 분들이 포인터에 기본적인 방법들인 pA는 주소값을 저장한다던가 *pA가 그 주소가 가리키는 값이라는 것은 쉽게 이해하지만

배열을 포인터변수에 집어넣는것과 같은 곳에서 많이 난해해 할것이다. 이것을 좀더 이해하기 쉽게 하는 것은 아래와 같은 방식으로 이

해 해보도록 하자.

int a[2];
a[0] = 11110;
a[1] = 4440;
char *pA;
pA   = (char*)a;

기존에 보던 방식과 약간다른 점이 있다. int형 배열의 시작주소인 배열의 이름 a를 char형 포인터 변수 pA에 삽입한다. 이것은 단순하게

이해하는데 도움을 주고자 하는것이기에 컴파일 오류를 없애기 위해 (char*)가 들어갔다고만 생각하고, 단순하게 int형 배열의 주소를

char형 포인터 변수에 저장했다고만 생각하자

이것을 실행한 결과화면은 아래와 같다.

c_14_2_8

a의 값은 배열의 시작주소이기에 pA에 저장된 값인 0x0012FF78일것이다.

그렇다면 *pA는 char형 포인터변수가 가리키는 값을 의미한다. 메모리상에서 0x0012FF78 번지의 값인 66(16진수), 102(10진수) 가 출력

되는것을 확인할수 있다.

여기에 pA+1은 char형의 크기만큼 즉 1byte만큼 주소를 증가 시킨값이기에 0x0012FF79번지 가 된다. 그 번지에 저장된 값은 2B(16진수)

즉 10진수 43이다. 마찬가지로 pA+4는 0x0012FF78+4 = 0x0012FF7C번지를 뜻하면 그 값은 58(16진수), 10진수로는 88이다.

위와 같이 포인터라는 것은 메모리의 주소를 프로그래머가 손쉽게 접근할수 있도록 해준다.

포인터와 관련된 소스코드를 보면서 이해하기에는 너무 난해한 어려움이 있다.

그것을 위 예제와 같이 메모리를 직접 그려보고 실제 메모리 주소에서 데이터 값이 어떻게 변하는지를 생각한다면 조금은 이해

하는데 도움이 될 수 있을것이다.