C언어 포인터 산술 (Pointer Arithmetic)
요약
C언어 포인터 산술의 핵심 개념을 알아봅니다. 포인터에 정수를 더하거나 빼는 연산, 문자열 포인터 산술, 배열과 포인터의 관계를 정보처리기사 실기 대비 핵심 내용으로 정리합니다.
포인터 산술 핵심 정리
| 개념 | 설명 | 예시 |
|---|---|---|
| 포인터 + 정수 | 포인터를 n칸 이동 | p + 1 |
| 포인터 - 정수 | 포인터를 n칸 뒤로 이동 | p - 1 |
| *(p + n) | p에서 n칸 이동한 위치의 값 | *(p + 2) = p[2] |
| 배열 이름 | 첫 번째 요소의 주소 (포인터처럼 동작) | arr = &arr[0] |
포인터 산술이란?
포인터 산술(Pointer Arithmetic) 은 포인터에 정수를 더하거나 빼서 메모리 주소를 이동하는 연산입니다.
포인터에 1을 더하면 다음 요소의 주소로 이동합니다. 이때 이동하는 바이트 수는 포인터가 가리키는 자료형의 크기에 따라 결정됩니다.
자료형에 따른 이동 크기
포인터에 1을 더하면, 가리키는 자료형의 크기만큼 주소가 증가합니다.
| 자료형 | 크기 | p + 1의 이동량 |
|---|---|---|
char | 1바이트 | +1바이트 |
int | 4바이트 | +4바이트 |
double | 8바이트 | +8바이트 |
int 배열과 포인터 산술
기본 이동
| 표현 | 동일한 표현 | 값 |
|---|---|---|
*p | p[0] = arr[0] | 10 |
*(p + 1) | p[1] = arr[1] | 20 |
*(p + 2) | p[2] = arr[2] | 30 |
시작 위치가 다른 경우
포인터가 배열의 중간을 가리킬 수도 있습니다.
| 표현 | 동일한 표현 | 값 |
|---|---|---|
*(p - 1) | arr[1] | 20 |
*p | arr[2] | 30 |
*(p + 1) | arr[3] | 40 |
배열 이름과 포인터 산술
배열 이름은 첫 번째 요소의 주소입니다. 따라서 배열 이름에 직접 포인터 산술을 사용할 수 있습니다.
a[i]와 *(a + i)는 같다
C언어에서 배열 접근 a[i]는 내부적으로 *(a + i)로 변환됩니다. 이 두 표현은 완전히 동일합니다.
| 배열 표기법 | 포인터 표기법 | 의미 |
|---|---|---|
arr[0] | *(arr + 0) | 배열의 0번째 요소 |
arr[1] | *(arr + 1) | 배열의 1번째 요소 |
arr[i] | *(arr + i) | 배열의 i번째 요소 |
이 관계를 알면 시험에서 *(arr + 2)가 나왔을 때 arr[2]로 바꿔서 빠르게 풀 수 있습니다.
문자열 포인터 산술
문자열은 char 배열이므로, 포인터 산술로 특정 위치부터의 부분 문자열에 접근할 수 있습니다.
문자열 리터럴과 포인터
문자열 리터럴 "KOREA"는 메모리에 저장된 char 배열의 시작 주소입니다.
%s로 출력하면 해당 위치부터 끝까지
printf의 %s는 주어진 주소부터 널 문자('\0')까지 출력합니다. 포인터 산술로 시작 위치를 바꾸면 부분 문자열을 출력할 수 있습니다.
| 표현 | 시작 위치 | 출력 (%s) |
|---|---|---|
p | p[0] = 'K' | "KOREA" |
p + 1 | p[1] = 'O' | "OREA" |
p + 3 | p[3] = 'E' | "EA" |
%c로 출력하면 해당 위치의 한 문자
%s와 달리 %c는 한 문자만 출력합니다. 이때 *(p + n) 또는 p[n]으로 특정 위치의 문자를 가져옵니다.
*(p + n)과 *p + n의 차이
괄호 위치에 따라 의미가 완전히 달라집니다.
| 표현 | 해석 | 결과 |
|---|---|---|
*(p + 4) | p를 4칸 이동한 위치의 값 | 'E' |
*p + 4 | p가 가리키는 값(A)에 4를 더함 | 'E' |
두 번째 *p + 4는 'A'의 ASCII 코드 65에 4를 더한 69, 즉 'E'입니다. 이 예시에서는 우연히 결과가 같지만, 원리가 다릅니다.
| 표현 | 연산 순서 | 의미 |
|---|---|---|
*(p + n) | 먼저 주소 이동, 그 다음 역참조 | p[n]의 값 |
*p + n | 먼저 역참조, 그 다음 정수 덧셈 | p[0]의 값 + n |
구조체 포인터 산술
구조체 배열에서도 포인터 산술이 동일하게 적용됩니다. 구조체 포인터에 1을 더하면 다음 구조체 요소로 이동합니다.
| 표현 | 동일한 표현 | 의미 |
|---|---|---|
p->id | s[0].id | 1 |
(p+1)->id | s[1].id | 2 |
(p+2)->name | s[2].name | "Park" |
구조체 멤버가 문자열 포인터일 때
구조체 멤버가 char * 타입이면, 해당 멤버에도 포인터 산술을 적용할 수 있습니다.
여기서 p->g는 "DC"의 시작 주소이고, p->i는 2입니다.
p->g + 1은 문자열 "DC"의 시작 주소에서 1칸 이동한 것이므로, 두 번째 문자 'C'의 주소를 가리킵니다. %s로 출력하면 "C"가 출력됩니다.
여기서 p->g + 1이 (p->g) + 1로 해석되는 이유는 연산자 우선순위 때문입니다. -> 연산자는 +보다 우선순위가 높아서, 먼저 p->g로 멤버에 접근한 뒤 + 1로 포인터 산술이 적용됩니다.
포인터 증감 연산자
p++와 p--도 포인터 산술입니다. 포인터를 한 칸씩 이동시킵니다.
*p++와 (*p)++의 차이
| 표현 | 해석 | 의미 |
|---|---|---|
*p++ | *(p++) | p가 가리키는 값을 읽고, p를 다음으로 이동 |
(*p)++ | p가 가리키는 값에 후위 증가 | p가 가리키는 값을 1 증가 |
++*p | ++(*p) | p가 가리키는 값을 1 증가 (전위) |
*p++는 *(p++)로 해석됩니다. 후위 ++이므로 p가 현재 가리키는 값을 반환한 후 포인터가 다음 요소로 이동합니다.