C 언어에서 배열(Array)은 같은 자료형의 요소들을 연속된 메모리 공간에 저장하는 자료구조입니다. 반면 포인터(Pointer)는 특정 변수의 메모리 주소를 저장하는 주소 변수입니다.
흥미로운 점은 배열 변수 자체도 하나의 주소를 가지고 있다는 것입니다. 따라서 포인터가 배열의 시작 주소를 가리킬 수 있으며, 포인터 연산을 통해 배열의 각 요소를 자유롭게 탐색할 수 있습니다.
배열을 포인터로 다루는 이유
배열을 포인터로 활용하면 두 가지 큰 장점이 있습니다.
동적 할당 배열의 관리: 런타임에 동적으로 할당된 배열의 주소를 포인터에 저장하여 유연하게 사용할 수 있습니다.
함수 전달: 함수에 배열을 인수로 전달할 때, 실제로는 배열 전체가 복사되는 것이 아니라 배열의 시작 주소(포인터)가 전달됩니다.
배열과 포인터의 주요 차이점
sizeof() 연산자: 배열에 사용하면 배열 전체의 크기(바이트 단위)를 반환하지만, 포인터에 사용하면 포인터 변수 자체의 크기(int 크기, 일반적으로 32비트 시스템에서는 4바이트, 64비트 시스템에서는 8바이트)를 반환합니다.
대입(assignment): 배열 변수는 다른 변수의 주소를 대입받을 수 없지만, 포인터는 어떤 변수의 주소든 자유롭게 저장할 수 있습니다.
첫 번째 값(first value): 포인터가 가리키는 값은 배열의 첫 번째 요소와 같습니다. 즉,
array[0] == *p가 성립합니다.반복(iteration): 배열은 인덱스([])를 사용해 요소에 접근하고, 포인터는 포인터 산술 연산으로 요소에 접근합니다. 예를 들어
array[2] == *(p+2)가 성립합니다.
예제 코드 (C)
#include <stdio.h>
void printElement(char* q, int index){
printf("Element at index(%d) is: %c\n", index, *(q+index));
}
int main() {
char arr[] = {'A', 'B', 'C'};
char* p = arr;
printf("Size of arr[]: %d\n", sizeof(arr));
printf("Size of p: %d\n", sizeof(p));
printf("First element using arr is: %c\n", arr[0]);
printf("First element using p is: %c\n", *p);
printf("Second element using arr is: %c\n", arr[1]);
printf("Second element using p is: %c\n", *(p+1));
printElement(p, 2);
return 0;
}실행 결과
Size of arr[]: 3 Size of p: 8 First element using arr is: A First element using p is: A Second element using arr is: B Second element using p is: B Element at index(2) is: C
위 실행 결과를 보면 sizeof(arr)는 배열의 실제 크기인 3바이트를 반환한 반면, sizeof(p)는 포인터 변수 자체의 크기인 8바이트(64비트 환경 기준)를 반환합니다. 또한 배열의 인덱스 접근(arr[1])과 포인터 산술 접근(*(p+1))이 동일한 결과를 보여주는 것을 확인할 수 있습니다.