C++ Segmentation Fault | "세그멘테이션 폴트" 크래시 원인과 해결

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란?
  2. 원인 10가지
  3. 디버깅 방법
  4. 예방 방법
  5. 정리

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 초기화
이중 해제스마트 포인터

핵심 규칙

  1. 스마트 포인터 사용 (댕글링 포인터 방지)
  2. nullptr 체크 (널 포인터 방지)
  3. at() 사용 (범위 체크)
  4. ASan 활성화 (디버깅)

체크리스트

  • 포인터 사용 전에 nullptr 체크를 하는가?
  • 스마트 포인터를 사용하는가?
  • 배열 범위를 체크하는가?
  • ASan으로 테스트하는가?

같이 보면 좋은 글 (내부 링크)

이 주제와 연결되는 다른 글입니다.

  • C++ 포인터 기초 | Pointer 완벽 가이드
  • C++ 미정의 동작 | Undefined Behavior
  • C++ 스택 오버플로우 | Stack Overflow 해결
  • C++ 스마트 포인터 | unique_ptr·shared_ptr

마치며

Segmentation Fault잘못된 메모리 접근으로 발생하는 크래시입니다.

핵심 원칙:

  1. 스마트 포인터 사용
  2. nullptr 체크
  3. ASan 활성화

원시 포인터보다 스마트 포인터를 사용해 안전하게 코딩하세요.

다음 단계: Segmentation Fault를 이해했다면, C++ 스마트 포인터 가이드에서 더 깊이 배워보세요.


관련 글

  • C++ 시리즈 전체 보기
  • C++ Adapter Pattern 완벽 가이드 | 인터페이스 변환과 호환성
  • C++ ADL |
  • C++ Aggregate Initialization |