200===Dev Language/C

C 언어 포인터에 대한 가장 명확한 해석 및 설명 🎯

블로글러 2025. 5. 8. 20:54

아래의 문제를 받았을 때 어떻게 해석해야할까요?
지금부터 가장 간결하게 쉽게 포인터를 이해할 수 있게 풀어보겠습니다.

문제 :

#include <stdio.h>
void func(int **p);
int main() {
    int a = 10;
    int *ptr = &a;
    func(&ptr);
    printf("%d\n", *ptr);
    return 0;
}
void func(int **p) {
    int b = 20;
    *p = &b;
}

🎯 1단계: 초기화와 메모리 상태

코드

int main() {
    int a = 10;
    int *ptr = &a;

이해하기 쉬운 설명

  • 정수 변수 a가 선언되어 값 10을 저장합니다.
  • 포인터 ptr은 변수 a주소를 저장하여, a를 가리킵니다.

메모리 모습

+------------+         +-------------+
| 변수 이름  |  값     |   주소값     |
+------------+---------+-------------+
|     a      |  10     |  0x100      |
+------------+---------+-------------+
|    ptr     | 0x100   |  0x200      |
+------------+---------+-------------+
  • a의 주소는 예를 들어 0x100이라고 하면,
    ptr0x100이라는 값을 가짐으로써, 변수 a를 가리키게 됩니다.

🎯 2단계: 포인터의 주소 전달 (func(&ptr))

코드

func(&ptr);

이해하기 쉬운 설명

  • 여기서 포인터 ptr주소값을 함수에 전달합니다.
  • 이로 인해 함수 내에서 포인터의 값(ptr의 값, 즉 가리키는 위치)을 바꿀 수 있게 됩니다.

왜 포인터의 주소를 전달할까요?

  • 포인터를 통해 가리키는 주소를 함수 내에서 바꾸고 싶기 때문입니다.

메모리 모습 (함수로 주소 전달)

함수 func가 호출되며 포인터 ptr의 주소(0x200)를 전달함.

main의 메모리:
+------------+---------+-------------+
| 변수 이름  |  값     |   주소값     |
+------------+---------+-------------+
|     a      |  10     |  0x100      |
+------------+---------+-------------+
|    ptr     | 0x100   |  0x200      | <--- 이 주소(0x200)를 전달
+------------+---------+-------------+

🎯 3단계: 함수 내부에서 포인터 변경

코드

void func(int **p) {
    int b = 20;
    *p = &b;
}

이해하기 쉬운 설명

  • 함수 내부에서 지역변수 b가 선언되어 값이 20으로 설정됩니다.
  • p는 전달받은 ptr의 주소(0x200)를 가리킵니다.
  • *p = &b의 의미는 다음과 같습니다:
    • "포인터 ptr의 값을 b의 주소로 바꿔라"
      즉, ptr은 이제 b를 가리키게 됩니다.

메모리 모습 (함수 실행 중)

func의 메모리:
+------------+---------+-------------+
| 변수 이름  |  값     |   주소값     |
+------------+---------+-------------+
|     b      |  20     |  0x300      |
+------------+---------+-------------+
|     p      | 0x200   |  0x400      |
+------------+---------+-------------+

위 상황에서:
*p = &b → 주소 0x200에 있는 ptr의 값을 b의 주소인 0x300으로 변경.

main의 메모리 상태 변화:
+------------+---------+-------------+
| 변수 이름  |  값     |   주소값     |
+------------+---------+-------------+
|    ptr     | 0x300   |  0x200      | <-- 바뀜! (b의 주소로 변경됨)
+------------+---------+-------------+

🎯 4단계: 함수가 끝난 후 문제점(Dangling Pointer) 발생

이해하기 쉬운 설명

  • 함수 func가 끝나면, 지역변수 b의 메모리(0x300)는 소멸됩니다.
  • 그런데 메모리가 소멸된 후에도 ptr여전히 소멸된 변수 b의 주소(0x300)를 가리키고 있습니다.
  • 이렇게 되면 포인터가 가리키는 값은 **불확실한 쓰레기값(garbage)**이 됩니다.

메모리 모습 (함수 종료 직후)

main에서 포인터 ptr 상태:
+------------+---------+-------------+
| 변수 이름  |  값     |   주소값     |
+------------+---------+-------------+
|    ptr     | 0x300   |  0x200      |  <-- b는 소멸된 메모리!
+------------+---------+-------------+

[문제발생] 이제 ptr은 소멸된 메모리를 가리키고 있음 → Dangling Pointer

🎯 5단계: main에서 문제 있는 값 출력하기

코드

printf("%d\n", *ptr);

이해하기 쉬운 설명

  • 포인터 ptr이 가리키는 메모리는 이제 유효하지 않습니다.
  • 따라서 여기서 출력되는 값은 예측 불가능한 쓰레기값입니다.
  • 운이 좋으면 이전 값(20)이 나올 수도 있지만, 대부분 엉뚱한 값 또는 프로그램 에러가 발생할 수 있습니다.

실제 출력 예시 (환경에 따라 달라짐)

178283832 (쓰레기 값 예시)
또는 Segmentation fault (심각한 오류)

🎯 6단계: 올바른 수정법 예시

수정 예시 1: 함수에서 값을 직접 수정 (안전한 방법)

#include <stdio.h>

void func(int *p) {
    *p = 20;  // 직접 값을 변경
}

int main() {
    int a = 10;
    func(&a);          // 변수 a의 주소를 넘겨 직접 변경
    printf("%d\n", a); // 정상 출력: 20
    return 0;
}

메모리:

main에서 a가 직접 변경됨.
+------------+---------+
| 변수 이름  |  값     |
+------------+---------+
|     a      |  20     | <-- 직접 변경됨
+------------+---------+

📌 최종 요약 및 주의사항:

  • 원본 코드의 문제점은 지역변수의 주소를 main에서 사용하고 있습니다.
  • 지역변수는 함수 종료와 함께 사라지므로, 주소를 main에서 절대 사용하면 안 됩니다.
  • 안전한 방법은 함수 내에서 직접 값만 변경하거나, 정적(static) 또는 동적(malloc) 변수를 사용해야 합니다.

초보자 주의사항:

  • 항상 포인터가 가리키는 변수가 유효한지 확인하고 사용해야 합니다.
  • 함수 내부의 지역변수의 주소는 절대 반환하거나 외부에 넘기지 않도록 주의하세요.
728x90
반응형