본문 바로가기

programming language/C 언어

c언어 포인터와 배열

#include<stdio.h>

int main(void)
{
	int num = 7;
	int* pnum;
	pnum = &num;
	........
}

포인터 변수란? 메모리의 주소 값을 저장하기 위한 변수이다.

그러면 여기서 포인터 변수는 *pnum 일까, pnum일까? 바로 pnum이다.

위 코드를 보면 num의 주소 값을 포인터 변수인 pnum에 저장하고 있는 것을 볼 수 있다.

 

이러면, num의 시작 번지 주소 값이 포인터 변수 pnum에 저장이 되고,

이를 포인터 변수 pnum이 int형 변수 num을 가리킨다.라고 할 수 있다.

 

그렇다면 *pnum은 무엇일까? 우선,* 연산자는 포인터가 가리키는 메모리 공간에 접근할 때, 사용하는 연산자이다.

그러므로, *pnum포인터 변수 pnum이 가리키는 메모리 공간인 변수 num에 접근을 해서..라고 생각할 수 있다.

만약 위 코드에서 *pnum = 20; 과 같은 문장이 있었다면, 이는 포인터 변수 pnum이 가리키는 메모리 공간인 변수 num에 정수 20을 저장한다. 라는 것을 알 수 있다.

 

즉, 위 코드에서 포인터 변수는 *pnum이 아니라, pnum이고 *pnum은 포인터 변수 pnum이 가리키는 메모리 공간에 접근..이라는 뜻을 가지고 있다. 위 코드 블록에서 *pnum의 값은  7이다.

 


배열의 이름은 포인터이다. 단, 그 값을 바꿀 수 없는 '상수 형태의 포인터이다.

 

(% p는 주소 값의 출력에 사용되는 서식 문자이다.)

#include<stdio.h>

int main(void){

int arr[3] = { 0,1,2 };
printf("배열의 이름: %p\n", arr);
printf("첫 번쨰 요소: %p\n", &arr[0]);
printf("두 번쨰 요소: %p\n", &arr[1]);
printf("세 번쨰 요소: %p\n", &arr[2]);

return 0;
}

 

실행결과, 배열의 이름의 주소 값과 배열 첫 번째 요소의 주소 값이 같은 것을 알 수 있다.

 

 cf) arr = &arr [i]의 문장이 가능할까? 불가능하다.

&연산자의 피연산자는 변수여야 하며, 상수는 피연산자가 될 수 없다. 즉 배열의 이름은 대입 연산자의 피연산자가 될 수 없다. 그러므로 결론은 배열의 이름은 배열의 시작 주소 값을 의미하며, 그 형태는 값의 저장이 불가능한 상태이다. 

배열의 이름은 '상수 형태의 포인터'이고 '포인터 상수'라고 부르기도 한다.


배열의 이름을 대상으로 하는 * 연산

#include<stdio.h>

int main(void){

int arr1[3] = { 1,2,3 };
double arr2[3] = { 1.1,2.2,3.3 };

printf("%d %g\n", *arr1, *arr2);
*arr1 += 100;
*arr2 += 120.5;
printf("%d %g\n", arr1[0], arr2[0]);
return 0;
}

 

printf("% d % g\n", *arr1, *arr2); arr1과 arr2가 가리키는 대상을 출력하고 있는 문장이다. 

arr1은 int형 포인터이므로 4바이트 크기의 메모리를 정수의 형태로 참조하고,

arr2은 double형 포인터이므로 8바이트 크기의 메모리를 실수의 형태로 참조하게 된다.

따라서, arr1과 arr2가 가리키는 첫 번째 요소가 출력이 된다.

arr1 [3] = {2,1,3}으로 바꾸면, *arr1 = 2가 된다. arr1의 배열의 첫 번째 요소가 2로 바뀌었기 때문.

 

포인터 연산

#include<stdio.h>

int main(void){

int arr1[3] = { 11,22,33 };
int* ptr = arr1;  //int *ptr = &arr[0];과 같은 문장.
printf("%d %d %d\n", *ptr, *(ptr + 1), *(ptr + 2));

printf("%d", *ptr); ptr++;
printf(" %d", *ptr); ptr++;
printf(" %d", *ptr); ptr--;
printf(" %d", *ptr); ptr--;
printf(" %d", *ptr); printf("\n");
return 0;
}

 

*(ptr + 1) : 포인터 변수 ptr은 int형 포인터 값을 1 증가시키는 연산을 할 때마다 실제로는 4가 증가한다. 

 

포인터를 대상으로 하는 증가 연산의 결과

int형 포인터를 대상으로 n 증가 >>>>n x sizeof(int)의 크기만큼 증가. 즉, 1을 증가시키는 연산을 하면 4 증가.

 

<참고>

우리가 선언한 배열이 int형 배열이고 각 요소 별로 할당되는 메모리 공간의 크기는 4바이트이다. 따라서 배열 요소 간 주소 값의 차이는 4 바이트이다.

 

*(ptr + 1)과 *(++ptr)의 차이는 무엇 일까? 

 

*(ptr + 1)는 +로 인해서 ptr에 저장된 값이 증가하지 않는다. 단, 증가된 값을 연산의 결과로 얻어서 * 연산을 진행하는 것뿐. 즉, 현재 배열 요소의 값(0x0012 ff50)에서 다음 배열 요소의 주소 값을 나타낼 때 ptr = 0x0012 ff54가 아닌,

ptr +1 = 0x0012 ff54로 나타낸 다는 것이다. ptr에 저장된 값은 변경되지 않는다!

 

*(++ptr)는 ++연산의 결과로 포인터 변수 ptr에 저장된 값이 4만큼 증가한다. 즉, 현재 배열 요소의 주소 값에서 다음 배열 요소의 주소 값을 나타낼 때, ptr = 0x0012 ff54로 나타내어진다. ptr에 저장된 값 자체를 변경한 것 이기 때문이다.

 


포인터는 변수 형태의 포인터 상수 형태의 포인터를 어우르는 표현인데,

그러면 포인터 그 자체의 뜻은 뭘까? 포인터는 (주소 값을) 가리키는 주체라고 생각한다.