
C 배열 포인터 차이는 C 언어를 배우는 사람이 가장 오래 헷갈리는 주제입니다. `arr`을 출력하면 주소처럼 보이고, 함수에 넘기면 포인터처럼 동작해서 둘이 같은 것처럼 느껴집니다.
하지만 핵심은 배열은 저장 공간이고 포인터는 주소를 담는 변수라는 차이입니다. 이 글은 C 배열 선언, 포인터, sizeof, 함수 매개변수 규칙을 기준으로 `arr`과 `&arr`이 왜 다르게 보이는지 정리합니다.

C 배열 포인터 차이를 먼저 나누면
배열은 여러 원소가 연속해서 놓인 저장 공간입니다. 포인터는 어떤 객체의 주소를 저장하는 변수입니다. 이 차이를 잡고 있어야 `arr`, `&arr`, `sizeof` 결과를 이해할 수 있습니다.
int arr[3] = {10, 20, 30};
int *p = arr;
printf("%d\n", p[0]); // 10위 코드에서 `arr`은 많은 식에서 첫 원소 주소처럼 변환됩니다. 그래서 `int *p = arr;`가 가능합니다. 하지만 이것이 배열 자체가 포인터 변수라는 뜻은 아닙니다.
array decay는 배열이 포인터처럼 보이는 핵심 이유
C에서는 배열 표현식이 여러 상황에서 첫 번째 원소를 가리키는 포인터로 변환됩니다. 이 현상을 보통 array decay라고 부릅니다.
int arr[3] = {10, 20, 30};
printf("%p\n", (void *)arr); // 첫 원소 주소처럼 보임
printf("%p\n", (void *)&arr[0]); // 첫 원소 주소그래서 `arr`과 `&arr[0]`은 주소 값이 같아 보일 수 있습니다. 하지만 타입 관점에서는 여전히 배열 표현식이 포인터로 변환된 결과입니다.
sizeof arr과 sizeof pointer는 왜 다를까
배열이 정말 포인터 변수라면 `sizeof arr`과 `sizeof pointer`가 같은 크기여야 합니다. 하지만 실제로는 다릅니다.
int arr[3] = {10, 20, 30};
int *p = arr;
printf("%zu\n", sizeof arr); // int 3개 크기
printf("%zu\n", sizeof p); // 포인터 변수 크기`sizeof arr`은 배열 전체 크기를 봅니다. 반면 `sizeof p`는 포인터 변수 하나의 크기를 봅니다. 이 차이는 배열과 포인터가 같은 것이 아니라는 강한 증거입니다.
arr과 &arr은 주소 값이 같아도 타입이 다르다
`arr`은 많은 식에서 `int *`처럼 쓰입니다. 하지만 `&arr`은 배열 전체를 가리키는 포인터입니다. 주소 숫자는 같아 보일 수 있지만, 포인터 산술에서 차이가 납니다.
int arr[3] = {10, 20, 30};
printf("%p\n", (void *)(arr + 1)); // 다음 int 원소
printf("%p\n", (void *)(&arr + 1)); // 배열 전체 다음 위치`arr + 1`은 int 하나만큼 이동합니다. `&arr + 1`은 int 3개짜리 배열 전체만큼 이동합니다. 그래서 두 표현식은 타입을 함께 봐야 이해됩니다.
함수 인자로 넘기면 배열 크기 정보가 사라진다
함수 매개변수에 `int arr[]`라고 써도 실제로는 `int *arr`처럼 조정됩니다. 그래서 함수 안에서 `sizeof arr`을 하면 배열 전체 크기가 아니라 포인터 크기가 나옵니다.
void print_size(int arr[]) {
printf("%zu\n", sizeof arr); // 포인터 크기
}
int main(void) {
int values[3] = {1, 2, 3};
print_size(values);
}함수에 배열을 넘길 때는 길이를 따로 같이 넘기는 습관이 중요합니다. C는 배열 길이를 자동으로 함께 전달하지 않습니다.
실무 체크리스트
- 배열은 저장 공간, 포인터는 주소를 담는 변수로 먼저 구분한다
- `sizeof` 결과가 헷갈리면 지금 배열 자체인지 포인터인지 확인한다
- `arr + 1`과 `&arr + 1`은 타입 기준 이동 단위가 다르다는 점을 기억한다
- 함수에 배열을 넘길 때는 원소 개수를 함께 넘긴다
- 문자열 배열과 포인터 배열에서는 타입을 종이에 써 보고 읽는다
정리
C 배열 포인터를 이해할 때는 문법 이름보다 문제를 일으키는 지점을 먼저 봐야 합니다. 오늘 글의 핵심은 겉으로 비슷해 보이는 코드가 어떤 기준으로 다르게 동작하는지 분리해 보는 것입니다.
함께 보면 좋은 내부 글은 C 언어 argc argv란 무엇일까, C 점 연산자 화살표 연산자 차이, C 언어 비트 연산자는 언제 쓸까입니다. 외부 기준은 cppreference – Array declaration, cppreference – Pointer, cppreference – sizeof operator를 확인했습니다.