아래의 문제를 받았을 때 어떻게 해석해야할까요?
지금부터 가장 간결하게 쉽게 포인터를 이해할 수 있게 풀어보겠습니다.
문제 :
#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
이라고 하면,ptr
은0x100
이라는 값을 가짐으로써, 변수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
반응형
'200===Dev Language > C' 카테고리의 다른 글
C언어 포인터와 배열 - 메모리 탐험 가이드 🗺️ (1) | 2024.06.09 |
---|---|
C 언어 포인터 완벽 가이드: 초보자부터 전문가까지 😎 (0) | 2024.05.29 |
C 언어의 세계로 떠나는 여행 🚀 (0) | 2024.05.26 |