08. 포인터 (Pointer): 메모리의 지배자

C언어의 꽃이자 끝판왕. 데이터의 '주소'를 직접 조작하는 마법을 배웁니다.

1. 포인터의 기초: 보물과 보물지도

지금까지 변수는 데이터를 담는 '상자'라고 배웠습니다. 포인터는 그 데이터 자체가 아니라, 상자가 메모리 어디에 있는지 '주소'를 기억하는 특별한 변수입니다. 마치 보물상자의 위치를 그려놓은 보물지도와 같습니다.

포인터 주소 개념도
▲ 포인터: "보물이 어디 있지?" 메모리 주소를 찾아가는 나침반

💡 포인터의 핵심 기호 2가지

& (주소 연산자): "이 상자의 주소가 어디야?" (위치 알아내기)
* (참조 연산자): "이 주소로 찾아가서 내용물을 꺼내와!" (보물 꺼내기)

🗺️ 기초 예제: 보물찾기

#include <stdio.h>

int main() {
    int gold = 1000;     // 실제 금이 들어있는 상자
    int *map = &gold;    // 금 상자의 주소(&)를 지도(포인터)에 저장

    printf("상자의 실제 내용물: %d골드\n", gold);
    printf("상자의 메모리 주소: %p\n", map); // %p는 주소 출력용 서식
    printf("지도를 보고 찾아간 내용물: %d골드\n", *map); // * 기호로 내용물 접근

    return 0;
}

2. 왜 포인터를 쓰나요? (Call by Reference)

가장 많이 묻는 질문입니다. "그냥 변수 쓰면 되지, 왜 굳이 주소를 넘기나요?"

함수에게 변수를 그냥 넘겨주면 '복사본'만 줍니다. 함수 안에서 복사본을 아무리 지지고 볶아도 원본은 바뀌지 않죠. 하지만 '주소'를 넘겨주면 함수가 원본을 직접 찾아와서 수정할 수 있습니다!

⚔️ 심화 예제 1: 두 변수의 값 바꾸기 (Swap 함수)

#include <stdio.h>

// 포인터(주소)를 매개변수로 받는 함수
void swap(int *a, int *b) {
    int temp = *a;  // a주소의 값을 잠시 보관
    *a = *b;        // b주소의 값을 a주소에 덮어쓰기
    *b = temp;      // 보관했던 값을 b주소에 넣기
}

int main() {
    int hp = 100;
    int mp = 50;

    printf("마법 사용 전 - HP: %d, MP: %d\n", hp, mp);
    
    // 변수의 '주소(&)'를 넘겨주어 원본을 바꿀 수 있게 허락합니다!
    swap(&hp, &mp); 
    
    printf("마법 사용 후 - HP: %d, MP: %d\n", hp, mp); // 값이 서로 바뀜!
    
    return 0;
}

3. 충격적인 진실: 배열의 진짜 정체

07장에서 배운 배열, 사실 그 배열의 이름 자체가 '포인터'였습니다! 배열의 이름은 항상 '첫 번째 상자(0번 칸)의 주소'를 가리키고 있습니다.

▲ 배열의 이름은 0번 인덱스의 메모리 주소를 담고 있는 포인터입니다.

📊 심화 예제 2: 포인터로 배열 다루기

#include <stdio.h>

int main() {
    int arr[3] = {10, 20, 30};
    int *ptr = arr; // 배열 이름(arr) 자체가 주소이므로 & 기호가 필요 없음!

    // 아래 3줄은 완벽하게 똑같은 결과를 출력합니다.
    printf("배열 첫 번째 값 (배열 방식): %d\n", arr[0]);
    printf("배열 첫 번째 값 (포인터 방식): %d\n", *ptr);
    printf("배열 첫 번째 값 (이름 직접 참조): %d\n", *arr);

    // 포인터의 덧셈: "다음 칸으로 이동해!"
    printf("배열 두 번째 값: %d\n", *(ptr + 1)); // arr[1]과 동일
    printf("배열 세 번째 값: %d\n", *(ptr + 2)); // arr[2]과 동일

    return 0;
}

💀 치명적인 함정: 허공을 가리키는 포인터

포인터가 아무것도 가리키지 않는 상태(쓰레기 주소)에서 *ptr = 100; 처럼 값을 넣으려고 하면 큰일 납니다! 이는 남의 집(메모리) 문을 부수고 들어가는 것과 같아서 컴퓨터가 즉시 프로그램을 강제 종료(Segmentation Fault)시킵니다. 포인터를 당장 쓰지 않을 때는 int *ptr = NULL; 이라고 초기화해 두는 습관을 들이세요.