BioPlayground

🧬
목록으로

포인터와 메모리 주소

포인터가 무엇인지, 메모리 주소가 왜 중요한지, 그리고 고급 언어에서 참조가 포인터와 어떻게 다른지 배웁니다.

중급
|
12
|
검증 완료 (2026-07)
포인터메모리 주소참조간접 접근NULL pointer
진행률0/13 (0%)

포인터와 메모리 주소

이 토픽을 마치면

포인터가 "메모리 주소를 저장하는 변수"라는 것을 이해하고, 왜 컴퓨터 과학에서 중요한지, 고급 언어에서는 어떻게 숨겨져 있는지 알게 됩니다.


변수는 어디에 있는가

프로그램이 실행되면 변수는 메모리에 저장됩니다. 메모리는 바이트 단위로 번호가 매겨져 있습니다. 이 번호가 메모리 주소입니다.

text
메모리 주소:  0x1000  0x1004  0x1008  0x100C
             ┌──────┐┌──────┐┌──────┐┌──────┐
             │  42  ││  7   ││ 'A'  ││  0   │
             └──────┘└──────┘└──────┘└──────┘
              변수 a   변수 b   변수 c   변수 d

a = 42라고 쓰면, 컴퓨터는 메모리 어딘가에 42를 저장하고, a라는 이름을 그 주소에 연결합니다. 우리는 주소를 신경 쓰지 않고 a라는 이름만 쓰면 됩니다.


포인터 — 주소를 저장하는 변수

일반 변수는 을 저장합니다. 포인터는 다른 변수의 주소를 저장합니다.

c
int a = 42;       // a에 42 저장
int *p = &a;      // p에 a의 주소 저장

printf("%d\n", a);    // 42 (a의 값)
printf("%p\n", p);    // 0x1000 (a의 주소)
printf("%d\n", *p);   // 42 (p가 가리키는 곳의 값)
text
변수 a          포인터 p
┌──────────┐   ┌──────────┐
│    42    │   │  0x1000  │──→ a를 가리킴
└──────────┘   └──────────┘
주소: 0x1000    주소: 0x2000
  • &a — a의 주소를 가져옴 (address-of)
  • *p — p가 가리키는 곳의 값을 가져옴 (dereference)

왜 포인터가 필요한가

1. 큰 데이터를 효율적으로 전달

1,000만 개의 숫자가 든 배열을 함수에 넘긴다고 합시다. 배열 전체를 복사하면 메모리와 시간이 낭비됩니다. 주소만 넘기면 원본에 직접 접근할 수 있습니다.

2. 동적 자료구조

연결 리스트, 트리, 그래프 — 이 자료구조들은 "다음 노드의 위치"를 저장해야 합니다. 그 "위치"가 포인터입니다.

text
노드 A              노드 B              노드 C
┌────┬────────┐   ┌────┬────────┐   ┌────┬──────┐
│ 10 │ 0x2000 │──→│ 20 │ 0x3000 │──→│ 30 │ NULL │
└────┴────────┘   └────┴────────┘   └────┴──────┘

3. 함수에서 원본 수정

c
void swap(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

int x = 10, y = 20;
swap(&x, &y);
// x = 20, y = 10

포인터 없이 값을 복사해서 넘기면 함수 안에서 아무리 바꿔도 원본은 그대로입니다. 포인터로 주소를 넘겨야 원본을 수정할 수 있습니다.


위험한 포인터

널 포인터

c
int *p = NULL;
printf("%d", *p);  // 크래시! (Segmentation Fault)

아무것도 가리키지 않는 포인터를 역참조하면 프로그램이 죽습니다. 이것이 null pointer dereference — 역사상 가장 비용이 큰 버그 유형 중 하나입니다.

댕글링 포인터

c
int *p = malloc(sizeof(int));
*p = 42;
free(p);        // 메모리 반환
printf("%d", *p);  // 이미 반환된 메모리 — 예측 불가

free로 메모리를 돌려준 뒤에 그 주소를 쓰면, 운이 좋으면 쓰레기 값이 나오고 운이 나쁘면 크래시합니다.


고급 언어에서의 포인터

Python, JavaScript, Java 같은 고급 언어에서는 포인터를 직접 다루지 않습니다. 하지만 **참조(reference)**라는 이름으로 같은 개념이 쓰입니다.

python
# Python에서의 참조
a = [1, 2, 3]
b = a # b는 같은 리스트를 "참조"
b.append(4)
print(a) # [1, 2, 3, 4] — 원본도 바뀜

Python의 변수는 사실 포인터입니다. 다만 &* 같은 문법이 없고, 메모리 주소를 직접 조작할 수 없을 뿐입니다. 이전 Python 토픽(가변 객체의 참조 전파)에서 다룬 문제가 바로 이것입니다.

C 포인터Python 참조
주소 직접 접근가능불가
포인터 연산가능 (p + 1)불가
NULL 크래시가능None → AttributeError
메모리 수동 해제필수 (free)자동 (가비지 컬렉션)

고급 언어는 포인터를 감추고 안전하게 만든 것입니다. 대가로 직접 메모리를 제어하는 능력을 포기합니다.


핵심

포인터는 메모리 주소를 저장하는 변수입니다. 큰 데이터 전달, 동적 자료구조, 원본 수정 — 이 세 가지 때문에 포인터가 필요합니다. Python/JavaScript의 "참조"는 포인터를 안전하게 감싼 것 — 주소 조작은 못 하지만 같은 원리로 작동합니다.