콘텐츠로 건너뛰기

포인터배열과 배열포인터

C언어 포인터에서 가장 헷갈리는 두 녀석이다.

핵심은 연산자 괄호와 우선순위다. 괄호는 묶음을 표시하며, [](배열)이 *(포인터)보다 우선순위가 높다는 것으로 해석하면 된다.

1. 포인터 배열 (Array of Pointers)

“나는 배열이다. 그런데 내용물이 주소(Pointer)”

int *arr[3];
// = int* arr[3];
// *가 자료형에 붙어서 표기하는 것이 정식표기 방법이며 이러면 자료형을 읽기 편하다.
  • 해석: int*이 작동한다. 즉, 인티저형 포인터란 의미. 그리고 arr[3]은 3개짜리 배열이다. 그러므로 안에 들어가는 것은 int*(주소)이다.
  • 구조: 포인터 변수 3개가 나란히 생성된다. 각각의 칸에 서로 다른 주소를 담을 수 있다.
  • 용도: 문자열 배열(char* str[]), 흩어져 있는 변수들을 묶어서 관리할 때, 행의 길이가 다른 2차원 배열(Jagged Array)을 만들 때 사용한다.

2. 배열 포인터 (Pointer to an Array)

“나는 포인터다. 그런데 가리키는 대상이 배열(Array)”

int (*arr)[3];
// int* (arr)[3]이 될 수 없다. *은 괄호로 묶여있기 때문
  • 해석: ()로 묶여있어 *가 밖으로 나올 수 없다. *arr $\rightarrow$ arr포인터다. $\rightarrow$ 무엇을 가리키나? int형 변수 3개가 모인 덩어리(배열)를 가리킨다.
  • 구조: 포인터 변수는 딱 1개만 생성된다. 이 포인터는 $int \times 3$ 크기의 메모리 블록을 통째로 가리킨다.
  • 용도: 2차원 배열을 함수의 인자로 넘길 때, 2차원 배열의 행 단위 이동이 필요할 때 사용한다.

3. 코드 실험 및 결론

작성한 코드를 통해 확인한 핵심 차이는 “초기화와 접근성”이다.

Case A: 포인터 배열

int *ptrArr[2];
// 각 칸마다 주소를 개별적으로 넣어줘야 함 (귀찮음)
ptrArr[0] = matrix[0]; 
ptrArr[1] = matrix[1];
  • 특징: 행(Row)들이 메모리상에 연속적이지 않아도 연결할 수 있다. 유연하지만 초기화가 번거롭다.

Case B: 배열 포인터

int (*arrPointer)[3] = matrix;
// 한 번에 2차원 배열 전체를 가리킬 수 있음 (편리함)
  • 특징: matrix 자체가 메모리에 연속적으로 배치된 2차원 배열이므로, 타입만 맞으면 한 번에 연결된다. arrPointer + 1을 하면 int 3개 크기(12바이트)만큼 점프하여 다음 행을 가리킨다.

요약

구분선언정체sizeof (32bit 기준)주 용도
포인터 배열int *p[3]배열 (주소 저장소)$4 \times 3 = 12$ byte문자열 배열, 비연속 메모리 관리
배열 포인터int (*p)[3]포인터 (배열 지시자)$4 \times 1 = 4$ byte2차원 배열 처리, 함수 인자 전달

결론:

함수에서 2차원 배열을 통째로 넘겨받아 행과 열을 누비며 작업해야 한다면, 배열 포인터(int (*p)[col])를 쓰는 것이 정석이다. 반면, 여러 개의 문자열이나 흩어진 변수들을 묶어서 관리하고 싶다면 포인터 배열이 답이다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다