개요
C 프로그래밍에서 포인터는 메모리 주소를 저장하는 강력한 도구입니다. 이 글에서는 포인터를 더욱 유연하게 활용할 수 있는 두 가지 개념, 즉 포인터 배열(Array of Pointers)과 이중 포인터(Pointer to Pointer)에 대해 자세히 알아보겠습니다.
1. 포인터 배열(Array of Pointers)
포인터 배열이란 이름 그대로 포인터들을 요소로 가지는 배열입니다. 일반 변수 배열과 마찬가지로, 여러 개의 주소 값을 하나의 배열로 묶어 관리할 수 있습니다.
선언 방법
데이터타입 *포인터이름[크기];
예를 들어 다음과 같이 선언합니다.
int *p[5]; // 정수형 요소의 주소 5개를 저장할 수 있는 포인터 배열
위 선언은 5개의 int형 변수 주소를 각각 저장할 수 있는 포인터 배열 p를 만듭니다.
초기화 방법
포인터 배열을 초기화할 때는 주소 연산자 &를 사용합니다. 배열 요소의 주소를 직접 대입하거나, 배열 이름(배열의 시작 주소)에 인덱스를 더하는 방식으로도 초기화할 수 있습니다.
int a[3] = {10, 20, 30};
int *p[3], i;
// 방법 1: 주소 연산자 사용
for (i = 0; i < 3; i++)
p[i] = &a[i];
// 방법 2: 배열 이름 + 인덱스 활용
for (i = 0; i < 3; i++)
p[i] = a + i;값 접근하기
포인터 배열에 저장된 주소가 가리키는 실제 값에 접근하려면 간접 참조 연산자 *(역참조 연산자)를 사용합니다.
for (i = 0; i < 3; i++)
printf("%d ", *p[i]);전체 예제 코드
#include <stdio.h>
int main() {
int a[3] = {10, 20, 30};
int *p[3], i;
// 배열 요소의 주소로 포인터 배열 초기화
for (i = 0; i < 3; i++)
p[i] = &a[i];
printf("배열의 요소들: ");
// 포인터 배열을 통해 값 출력
for (i = 0; i < 3; i++)
printf("%d \t", *p[i]);
return 0;
}실행 결과
배열의 요소들 : 10 20 30
2. 이중 포인터(Pointer to Pointer)
이중 포인터는 다른 포인터 변수의 주소를 저장하는 변수입니다. 일반 포인터가 변수의 주소를 저장한다면, 이중 포인터는 '포인터를 가리키는 포인터' 역할을 하며 별표(**) 두 개를 사용해 선언합니다.
선언 방법
데이터타입 **포인터이름;
예를 들어 다음과 같습니다.
int **q; // q는 포인터를 가리키는 포인터(이중 포인터)
초기화 방법
이중 포인터 역시 주소 연산자 &를 사용하여 초기화합니다. 먼저 일반 포인터가 변수의 주소를 받고, 그 포인터의 주소를 이중 포인터에 대입합니다.
int a = 10; int *p; int **q; p = &a; // p는 변수 a의 주소를 저장 q = &p; // q는 포인터 p의 주소를 저장
값 접근하기
이중 포인터로 최종 값에 접근하려면 간접 참조 연산자 *를 두 번 사용합니다. *q는 포인터 p의 주소에 해당하는 값을 의미하고, **q는 그 주소가 가리키는 실제 변수 a의 값이 됩니다.
전체 예제 코드
#include <stdio.h>
int main() {
int a = 10;
int *p;
int **q;
p = &a; // p는 a의 주소를 저장
q = &p; // q는 p의 주소를 저장
printf("a = %d\n", a);
printf("포인터를 통한 a의 값 = %d\n", *p);
printf("이중 포인터를 통한 a의 값 = %d\n", **q);
return 0;
}실행 결과
a = 10 포인터를 통한 a의 값 = 10 이중 포인터를 통한 a의 값 = 10
정리
포인터 배열은 여러 개의 주소를 배열 형태로 관리할 때 유용하며, 문자열 배열 처리나 2차원 배열 조작 등에 자주 활용됩니다. 반면 이중 포인터는 함수에서 포인터 자체를 수정해야 하거나 동적 메모리 할당 시 2차원 배열을 다룰 때 필수적인 개념입니다. 두 개념 모두 포인터와 주소의 관계를 명확히 이해하고 있으면 어렵지 않게 활용할 수 있습니다.