프로그래밍 언어/C언어

C언어 14. 포인터 배열, 다중 포인터

닉네임못짓는사람 2020. 7. 17. 18:31
반응형

우리는 최근 글에서 포인터란 무엇인지, 포인터의 기본적인 사용법에 대해 알아보았습니다.

이번엔 그 포인터를 사용하는 방법을 좀 더 폭넓게 알아보도록 하겠습니다.

포인터 배열


먼저 포인터 배열이란 것에 대해서 알아봅시다.

배열이란 특정한 자료형을 연속된 기억공간에 저장하는 것이라고 이미 알고 계실 겁니다.

그렇다면 물론 포인터도 배열에 저장할 수 있을 것입니다.

이렇게 배열의 요소가 포인터인 것을 포인터 배열이라고 말합니다.

char* ch_P[5];

포인터 배열의 선언방법은 위와같으며, 배열의 요소가 포인터인것을 알려주기위해 *를 사용합니다.

 

포인터배열의 각 요소는 포인터이기 때문에 ch_P배열에 다음과 같이 값을 입력할 수 있을 것입니다.

char* ch_P[5] = { "apple", "banana", "melon", "cherry", "lemon" };	//초기화시
ch_P[2] = "korea";					//3번째 요소의 주소값 변경

위처럼 초기화 시에 각 요소에 문자열 상수의 주소값을 입력할 수 있으며,

초기화 이후에도 주소값을 변경할 수 있습니다.

 

여기서 재밌는 점은 이런 포인터 배열이 우리가 이전에 알아본 2차원 배열과 비슷한 형태라는 것입니다.

그 이유는 2차원 배열의 각 요소는 1차원 배열을 가지고 있는데,

이것이 바로 1차원 배열의 시작 주소값이기 때문입니다.

 

보통 2차원 배열은 n*m형태로 표현하지만,

실제 메모리상에선 모두 연속적인 메모리 공간에 존재한다고 이야기했었습니다.(10. 배열 글에서)

2차원 배열을 생성하면 메모리 공간상에 6개의 연속된 공간을 차지하게 되며,

2차원 배열의 요소는 각각 1차원 배열의 가장 첫 번째 요소의 주소값을 가지고 있습니다.

쉽게 생각해 in[0]이 1차원 배열의 배열명이라고 생각하시면 될 것입니다.

 

그럼 이를 코드로 확인해볼까요?

#include<stdio.h>

int main() {
	char ch_P[2][3] = {"aaa", "bbb"};
	printf("ch_P[0]의 주소값 : %u\n", ch_P[0]);
	printf("ch_P[0]의 값 : %c\n", *ch_P[0]);
	printf("ch_P[1]의 주소값 : %u\n", ch_P[1]);
	printf("ch_P[1]의 값 : %c\n", *ch_P[1]);
	return 0;
}

코드로 확인해보면 좀 더 직관적으로 이 이야기가 이해될 것이라고 생각됩니다.

다시 처음으로 돌아가 이런 형태가 포인터 배열과 매우 흡사하다는 것입니다.

따라서 1차원 포인터 배열을 2차원 배열과 흡사하게 활용할 수 있을 것입니다.

코드로 확인해보도록 합시다.

#include<stdio.h>

int main() {
	int in1[3] = { 0, 1, 2 };
	int in2[3] = { 3, 4, 5 };
	int in3[3] = { 6, 7, 8 };
	int* in_P[3] = { in1, in2, in3 };
	int i, j;
	int size_P = sizeof(in_P) / sizeof(in_P[0]);
	int size_F = sizeof(in1) / sizeof(int);
	
	for (i = 0; i < size_P; i++) {
		for (j = 0; j < size_F; j++) {
			printf("in_P[%d][%d] = %d\n", i, j, in_P[i][j]);
		}
	}
    	return 0;
}

이처럼 포인터 배열을 2차원 배열처럼 활용할 수 있습니다.

하지만 1차원 포인터 배열은 어디까지나 1차원 배열이란 사실은 염두에 두시길 바랍니다.

다중 포인터


그럼 이제 다른 이야기로 넘어가서 다중 포인터에 대해서 이야기해보도록 합시다.

먼저 2중 포인터로 이야기를 시작해보자면, 포인터는 메모리상의 주소값을 가리키는데,

2중 포인터는 이 포인터를 다시 가리키는 것을 의미합니다.

 

예를 들어 int형 변수 in의 주소값이 100번지라고 생각해봅시다.

이것을 저장하는 포인터 변수 in_P가 있다면 이 또한 메모리상에서 특정한 공간을 가질 것입니다.

이 공간의 주소값을 가리키는 것이 바로 이중 포인터입니다.

#include<stdio.h>

int main() {
	int in = 10;
	int* in_P = &in;
	printf("in_P = %u\n", in_P);
	printf("&in_P = %u\n", &in_P);		//이중포인터
	printf("in = %d", **&in_P);		//이중포인터를 사용해 in값 출력
    	return 0;
}

코드에서 이중 포인터를 사용해서 in의 값을 출력하는 부분을 보시면

참조 연산자(*)를 두 개를 사용했는데, 이는 이중 포인터를 참조한 뒤 그 값을 다시 참조한다는 의미입니다.

 

그리고 이중 포인터 또한 주소 값이라면 이를 저장하는 포인터 변수 또한 존재할 것입니다.

이것이 바로 이중 포인터 변수입니다.

이중 포인터 변수의 기본적인 선언 방법은 아주 간단하게 참조 연산자(*)를 여러 개 써주시면 됩니다.

예를 들어 이중 포인터 변수라면 다음과 같이 해주시면 됩니다.

int in = 10;
int* in_P = &in;
int** in_Dp = &in_P;

이것을 3중, 4중, 그 이상으로 할 수 있는 것이 바로 다중 포인터입니다.

 

이해를 돕기 위해 책에 있는 예제 코드를 작성해보도록 하겠습니다.

#include<stdio.h>

void exchange_ptr(char**, char**);

int main() {
	char* ap = "success";
	char* bp = "failure";
	printf("ap -> %s, bp -> %s\n", ap, bp);
	exchange_ptr(&ap, &bp);
	printf("ap -> %s, bp -> %s\n", ap, bp);
	return 0;
}

void exchange_ptr(char** app, char** bpp) {
	char* tp;
	tp = *app;
	*app = *bpp;
	*bpp = tp;
}

위의 코드는 두 이중 포인터 변수의 값을 서로 변경함으로써

두 변수의 값이 바뀔 수 있도록 작성한 코드입니다.

때문에 직접 값을 참조하지 않고도 값을 변경할 수 있습니다.

이러한 이중 포인터의 사용법도 꼭 숙지해주시길 바랍니다.

 

오늘 글은 여기까지 하고 마치도록 하겠습니다.

다음 글에서는 메모리 동적 할당에 대해서 알아보도록 하겠습니다.

감사합니다.

반응형

'프로그래밍 언어 > C언어' 카테고리의 다른 글

C언어 16. 변수의 영역  (0) 2020.07.22
C언어 15. 메모리 동적 할당  (0) 2020.07.20
C언어 13. 문자열  (0) 2020.07.16
C언어 12. 배열과 포인터  (0) 2020.07.15
C언어 11. 포인터  (0) 2020.07.14