9. 포인터와 배열의 관계

header


1. 배열 이름의 정체

배열과 포인터는 매우 밀접한 관계를 가지고 있습니다. 사실 배열의 이름은 포인터입니다!

배열 이름은 첫 번째 요소의 주소

int arr[3] = {10, 20, 30};
arr[0] arr[1] arr[2]
10 20 30
주소: 0x100 주소: 0x104 주소: 0x108

배열 이름 arr은 배열의 첫 번째 요소 arr[0]의 주소를 의미합니다.

arr == &arr[0]  // 참

실습 1

#include <stdio.h>

int main() {
    int arr[3] = {10, 20, 30};

    printf("배열 이름 arr: %p\n", arr);
    printf("첫 번째 요소의 주소 &arr[0]: %p\n", &arr[0]);
    printf("두 번째 요소의 주소 &arr[1]: %p\n", &arr[1]);
    printf("세 번째 요소의 주소 &arr[2]: %p\n", &arr[2]);

    return 0;
}
실행 결과 보기 (예시)
배열 이름 arr: 000000D5BCDFF6E8
첫 번째 요소의 주소 &arr[0]: 000000D5BCDFF6E8
두 번째 요소의 주소 &arr[1]: 000000D5BCDFF6EC
세 번째 요소의 주소 &arr[2]: 000000D5BCDFF6F0
  • arr&arr[0]이 같은 주소
  • int형은 4바이트이므로 주소가 4씩 증가

배열 이름과 포인터의 차이

배열 이름은 포인터처럼 동작하지만 중요한 차이점이 있습니다:

int arr[3] = {10, 20, 30};
int *ptr = arr;  // 가능

ptr = ptr + 1;   // 가능 (포인터는 값 변경 가능)
arr = arr + 1;   // 불가능! (배열 이름은 상수)
⚠️ 중요한 차이점
포인터 변수: 값을 변경할 수 있음
배열 이름: 상수 포인터, 값 변경 불가

2. 포인터 연산

포인터는 특별한 방식으로 산술 연산을 수행합니다.

포인터 증가 연산

포인터를 1 증가시키면, 가리키는 자료형의 크기만큼 주소가 증가합니다.

int arr[3] = {10, 20, 30};
int *ptr = arr;  // ptr은 arr[0]을 가리킴
연산 가리키는 요소 주소 증가량
ptr arr[0] -
ptr + 1 arr[1] +4 (int 크기)
ptr + 2 arr[2] +8 (int 크기)

자료형별 포인터 증가

char *cptr;   // char는 1바이트
cptr + 1;     // 주소 +1

int *iptr;    // int는 4바이트
iptr + 1;     // 주소 +4

double *dptr; // double은 8바이트
dptr + 1;     // 주소 +8
포인터 연산의 핵심
포인터 + 1 = 주소 + (자료형 크기 × 1)
포인터 + n = 주소 + (자료형 크기 × n)

실습 2

#include <stdio.h>

int main() {
    int arr[5] = {10, 20, 30, 40, 50};
    int *ptr = arr;

    printf("=== 포인터 연산 ===\n");
    printf("ptr: %p, 값: %d\n", ptr, *ptr);
    printf("ptr+1: %p, 값: %d\n", ptr+1, *(ptr+1));
    printf("ptr+2: %p, 값: %d\n", ptr+2, *(ptr+2));
    printf("ptr+3: %p, 값: %d\n", ptr+3, *(ptr+3));
    printf("ptr+4: %p, 값: %d\n", ptr+4, *(ptr+4));

    return 0;
}
실행 결과 보기 (예시)
=== 포인터 연산 ===
ptr: 000000C8FFDFF700, 값: 10
ptr+1: 000000C8FFDFF704, 값: 20
ptr+2: 000000C8FFDFF708, 값: 30
ptr+3: 000000C8FFDFF70C, 값: 40
ptr+4: 000000C8FFDFF710, 값: 50

주소가 4바이트(int 크기)씩 증가합니다.

포인터 증감 연산자

int arr[3] = {10, 20, 30};
int *ptr = arr;

printf("%d\n", *ptr);    // 10
ptr++;                    // 다음 요소로 이동
printf("%d\n", *ptr);    // 20
ptr++;                    // 다음 요소로 이동
printf("%d\n", *ptr);    // 30
💡 포인터 연산 활용
ptr++: 다음 요소로 이동
ptr--: 이전 요소로 이동
ptr += 2: 2칸 앞으로 이동

3. 포인터로 배열 접근하기

포인터를 사용하면 배열을 두 가지 방법으로 접근할 수 있습니다.

배열 표기법 vs 포인터 표기법

int arr[3] = {10, 20, 30};
int *ptr = arr;

같은 의미의 표현들:

의미 배열 표기법 포인터 표기법
첫 번째 요소 값 arr[0] *arr 또는 *ptr
두 번째 요소 값 arr[1] *(arr+1) 또는 *(ptr+1)
세 번째 요소 값 arr[2] *(arr+2) 또는 *(ptr+2)

실습 3

#include <stdio.h>

int main() {
    int arr[5] = {1, 2, 3, 4, 5};
    int i;

    printf("=== 배열 표기법 ===\n");
    for (i = 0; i < 5; i++) {
        printf("arr[%d] = %d\n", i, arr[i]);
    }

    printf("\n=== 포인터 표기법 ===\n");
    for (i = 0; i < 5; i++) {
        printf("*(arr+%d) = %d\n", i, *(arr+i));
    }

    return 0;
}
실행 결과 보기
=== 배열 표기법 ===
arr[0] = 1
arr[1] = 2
arr[2] = 3
arr[3] = 4
arr[4] = 5

=== 포인터 표기법 ===
*(arr+0) = 1
*(arr+1) = 2
*(arr+2) = 3
*(arr+3) = 4
*(arr+4) = 5

포인터로 배열 순회하기

#include <stdio.h>

int main() {
    int arr[5] = {10, 20, 30, 40, 50};
    int *ptr = arr;
    int i;

    // 방법 1: 인덱스 사용
    for (i = 0; i < 5; i++) {
        printf("%d ", ptr[i]);
    }
    printf("\n");

    // 방법 2: 포인터 연산
    for (i = 0; i < 5; i++) {
        printf("%d ", *(ptr + i));
    }
    printf("\n");

    // 방법 3: 포인터 증가
    ptr = arr;  // 포인터 초기화
    for (i = 0; i < 5; i++) {
        printf("%d ", *ptr);
        ptr++;
    }
    printf("\n");

    return 0;
}
실행 결과 보기
10 20 30 40 50
10 20 30 40 50
10 20 30 40 50

4. 포인터 배열

포인터 배열은 포인터를 요소로 가지는 배열입니다.

포인터 배열 선언

int *parr[3];  // int형 포인터 3개를 저장하는 배열

구조:

parr[0] → int형 변수의 주소
parr[1] → int형 변수의 주소
parr[2] → int형 변수의 주소

포인터 배열 사용 예

#include <stdio.h>

int main() {
    int a = 10, b = 20, c = 30;

    // 포인터 배열 선언 및 초기화
    int *parr[3] = {&a, &b, &c};

    // 포인터 배열을 통한 접근
    printf("첫 번째 값: %d\n", *parr[0]);  // 10
    printf("두 번째 값: %d\n", *parr[1]);  // 20
    printf("세 번째 값: %d\n", *parr[2]);  // 30

    // 값 변경
    *parr[0] = 100;
    printf("변경된 a: %d\n", a);  // 100

    return 0;
}
실행 결과 보기
첫 번째 값: 10
두 번째 값: 20
세 번째 값: 30
변경된 a: 100

문자열 배열

포인터 배열은 여러 문자열을 저장할 때 유용합니다.

#include <stdio.h>

int main() {
    char *fruits[3] = {"Apple", "Banana", "Cherry"};
    int i;

    printf("=== 과일 목록 ===\n");
    for (i = 0; i < 3; i++) {
        printf("%d. %s\n", i+1, fruits[i]);
    }

    return 0;
}
실행 결과 보기
=== 과일 목록 ===
1. Apple
2. Banana
3. Cherry
포인터 배열의 활용
• 여러 변수의 주소를 한 번에 관리
• 문자열 배열 구현
• 가변 길이 데이터 처리

5. 종합 실습

문제 1 - 배열과 포인터 (기초)

문제 1

다음 코드의 실행 결과는?

포인터와 배열
#include <stdio.h>

int main() {
    int arr[3] = {5, 10, 15};

    printf("%d", *arr);

    return 0;
}

문제 2 - 포인터 연산 (기초)

문제 2

다음 코드의 실행 결과는?

포인터와 배열
#include <stdio.h>

int main() {
    int arr[4] = {2, 4, 6, 8};

    printf("%d", *(arr + 2));

    return 0;
}

문제 3 - 포인터 표기법 (중급)

문제 3

다음 코드의 실행 결과는?

포인터와 배열
#include <stdio.h>

int main() {
    int arr[5] = {10, 20, 30, 40, 50};
    int *ptr = arr + 2;

    printf("%d", *ptr + *(ptr + 1));

    return 0;
}

문제 4 - 포인터 증가 (중급)

문제 4

다음 코드의 실행 결과는?

포인터와 배열
#include <stdio.h>

int main() {
    int arr[4] = {1, 3, 5, 7};
    int *ptr = arr;

    ptr++;
    ptr++;

    printf("%d", *ptr);

    return 0;
}

문제 5 - 포인터 배열 (중급)

문제 5

다음 코드의 실행 결과는?

포인터와 배열
#include <stdio.h>

int main() {
    int a = 100, b = 200, c = 300;
    int *parr[3] = {&a, &b, &c};

    *parr[1] = 500;

    printf("%d", b);

    return 0;
}

문제 6 - 배열 합계 (고급)

문제 6

다음 코드의 실행 결과는?

포인터와 배열
#include <stdio.h>

int main() {
    int arr[5] = {2, 4, 6, 8, 10};
    int *ptr = arr;
    int sum = 0;
    int i;

    for (i = 0; i < 5; i++) {
        sum += *(ptr + i);
    }

    printf("%d", sum);

    return 0;
}

핵심 요약

1. 배열 이름과 포인터
• 배열 이름 = 첫 번째 요소의 주소
arr == &arr[0]
• 배열 이름은 상수 포인터 (값 변경 불가)

2. 포인터 연산
ptr + 1 = 다음 요소의 주소
• 주소 증가량 = 자료형 크기
ptr++, ptr-- 사용 가능

3. 배열 접근
• 배열 표기법: arr[i]
• 포인터 표기법: *(arr + i)
• 두 방법은 완전히 동일

4. 포인터 배열
• 선언: int *parr[크기];
• 포인터를 요소로 가지는 배열
• 문자열 배열 구현에 활용

5. 핵심 공식
arr[i] = *(arr + i)
&arr[i] = arr + i
ptr[i] = *(ptr + i)