C++ Segmentation Fault | "세그멘테이션 폴트" 크래시 원인과 해결
이 글의 핵심
C++ Segmentation Fault에 대한 실전 가이드입니다.
들어가며: “Segmentation fault (core dumped)"
"프로그램이 갑자기 크래시해요”
C++에서 Segmentation Fault(세그폴트)는 잘못된 메모리 접근으로 발생하는 크래시입니다. 널 포인터, 댕글링 포인터, 배열 범위 초과 등이 주요 원인입니다.
// ❌ 널 포인터 접근
int* ptr = nullptr;
*ptr = 42; // Segmentation fault
// ❌ 댕글링 포인터
int* ptr = new int(42);
delete ptr;
*ptr = 99; // Segmentation fault
// ❌ 배열 범위 초과
int arr[5];
arr[1000000] = 42; // Segmentation fault
이 글에서 다루는 것:
- Segmentation Fault 원인 10가지
- 디버깅 방법
- 예방 방법
- 실전 해결책
목차
1. Segmentation Fault란?
정의
Segmentation Fault는 프로세스가 접근 권한이 없는 메모리에 접근할 때 발생하는 크래시입니다.
// 메모리 레이아웃 (개념적)
// [텍스트] [데이터] [힙] [...] [스택]
// ↑
// 접근 금지 영역
int* ptr = (int*)0x12345678; // 임의의 주소
*ptr = 42; // ❌ Segmentation fault
플랫폼별 이름
- Linux/Unix: Segmentation fault (SIGSEGV)
- Windows: Access Violation (0xC0000005)
- macOS: EXC_BAD_ACCESS
2. 원인 10가지
원인 1: 널 포인터 접근
// ❌ 널 포인터
int* ptr = nullptr;
*ptr = 42; // Segmentation fault
// ✅ nullptr 체크
int* ptr = nullptr;
if (ptr != nullptr) {
*ptr = 42;
}
원인 2: 댕글링 포인터
// ❌ 댕글링 포인터
int* ptr = new int(42);
delete ptr;
*ptr = 99; // Segmentation fault
// ✅ delete 후 nullptr
int* ptr = new int(42);
delete ptr;
ptr = nullptr;
원인 3: 배열 범위 초과
// ❌ 범위 초과
int arr[5];
arr[100] = 42; // Segmentation fault
// ✅ 범위 체크
int arr[5];
int index = 100;
if (index >= 0 && index < 5) {
arr[index] = 42;
}
원인 4: 스택 오버플로우
// ❌ 무한 재귀
void foo() {
foo(); // Segmentation fault (스택 오버플로우)
}
// ✅ 종료 조건
void foo(int n) {
if (n <= 0) return;
foo(n - 1);
}
원인 5: 큰 지역 변수
// ❌ 스택에 큰 배열
void foo() {
int arr[10000000]; // Segmentation fault (스택 오버플로우)
}
// ✅ 힙 할당
void foo() {
auto arr = std::make_unique<int[]>(10000000);
}
원인 6: 지역 변수 반환
// ❌ 지역 변수 포인터 반환
int* foo() {
int x = 42;
return &x; // 댕글링 포인터
}
int main() {
int* ptr = foo();
*ptr = 99; // Segmentation fault
}
// ✅ 동적 할당
std::unique_ptr<int> foo() {
return std::make_unique<int>(42);
}
원인 7: 초기화되지 않은 포인터
// ❌ 초기화 안 됨
int* ptr; // 쓰레기 값
*ptr = 42; // Segmentation fault
// ✅ 초기화
int* ptr = nullptr;
// 또는
int* ptr = new int(42);
원인 8: 잘못된 캐스팅
// ❌ 잘못된 캐스팅
int x = 42;
std::string* ptr = (std::string*)&x;
ptr->length(); // Segmentation fault
// ✅ 올바른 타입 사용
int x = 42;
int* ptr = &x;
원인 9: 이중 해제
// ❌ 이중 해제
int* ptr = new int(42);
delete ptr;
delete ptr; // Segmentation fault
// ✅ delete 후 nullptr
int* ptr = new int(42);
delete ptr;
ptr = nullptr;
원인 10: 문자열 리터럴 수정
// ❌ 문자열 리터럴 수정
char* str = "Hello";
str[0] = 'h'; // Segmentation fault (읽기 전용)
// ✅ 배열 사용
char str[] = "Hello";
str[0] = 'h'; // OK
3. 디버깅 방법
방법 1: GDB
# 디버그 심볼로 컴파일
g++ -g -o myapp main.cpp
# GDB 실행
gdb ./myapp
# 프로그램 실행
(gdb) run
# 크래시 위치 확인
(gdb) backtrace
(gdb) frame 0
(gdb) print ptr
방법 2: Valgrind
# Valgrind 실행
valgrind --leak-check=full ./myapp
# 출력 예시:
# Invalid write of size 4
# at 0x4005A7: main (main.cpp:10)
# Address 0x0 is not stack'd, malloc'd or (recently) free'd
방법 3: AddressSanitizer
# ASan으로 컴파일
g++ -fsanitize=address -g -o myapp main.cpp
# 실행
./myapp
# 출력 예시:
# ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000
# #0 0x4005a7 in main main.cpp:10
4. 예방 방법
방법 1: 스마트 포인터 사용
// ✅ unique_ptr
auto ptr = std::make_unique<int>(42);
// 자동 해제, 댕글링 포인터 방지
// ✅ shared_ptr
auto ptr = std::make_shared<int>(42);
// 참조 카운팅, 자동 해제
방법 2: 컨테이너 사용
// ✅ vector (범위 체크)
std::vector<int> vec = {1, 2, 3};
vec.at(100); // 예외 던짐 (크래시 대신)
// ✅ array
std::array<int, 5> arr = {1, 2, 3, 4, 5};
arr.at(100); // 예외 던짐
방법 3: nullptr 체크
// ✅ nullptr 체크
void foo(int* ptr) {
if (ptr == nullptr) {
std::cerr << "Null pointer\n";
return;
}
*ptr = 42;
}
방법 4: 컴파일러 경고 활성화
# 모든 경고 활성화
g++ -Wall -Wextra -Werror main.cpp
# 경고 예시:
# warning: 'ptr' may be used uninitialized
정리
Segmentation Fault 원인
| 원인 | 해결책 |
|---|---|
| 널 포인터 | nullptr 체크 |
| 댕글링 포인터 | 스마트 포인터 |
| 배열 범위 초과 | at() 사용 |
| 스택 오버플로우 | 힙 할당 |
| 초기화 안 됨 | nullptr 초기화 |
| 이중 해제 | 스마트 포인터 |
핵심 규칙
- 스마트 포인터 사용 (댕글링 포인터 방지)
- nullptr 체크 (널 포인터 방지)
- at() 사용 (범위 체크)
- ASan 활성화 (디버깅)
체크리스트
- 포인터 사용 전에 nullptr 체크를 하는가?
- 스마트 포인터를 사용하는가?
- 배열 범위를 체크하는가?
- ASan으로 테스트하는가?
같이 보면 좋은 글 (내부 링크)
이 주제와 연결되는 다른 글입니다.
- C++ 포인터 기초 | Pointer 완벽 가이드
- C++ 미정의 동작 | Undefined Behavior
- C++ 스택 오버플로우 | Stack Overflow 해결
- C++ 스마트 포인터 | unique_ptr·shared_ptr
마치며
Segmentation Fault는 잘못된 메모리 접근으로 발생하는 크래시입니다.
핵심 원칙:
- 스마트 포인터 사용
- nullptr 체크
- ASan 활성화
원시 포인터보다 스마트 포인터를 사용해 안전하게 코딩하세요.
다음 단계: Segmentation Fault를 이해했다면, C++ 스마트 포인터 가이드에서 더 깊이 배워보세요.
관련 글
- C++ 시리즈 전체 보기
- C++ Adapter Pattern 완벽 가이드 | 인터페이스 변환과 호환성
- C++ ADL |
- C++ Aggregate Initialization |