본문으로 건너뛰기 C++ Segmentation Fault | '세그멘테이션 폴트' 크래시 원인과 해결

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

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

이 글의 핵심

C++ Segmentation Fault의 C++, Segmentation, Fault, 들어가며: "Segmentation fault core dumped"를 실전 예제와 함께 상세히 설명합니다.

들어가며: “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 초기화
이중 해제스마트 포인터

핵심 규칙

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

체크리스트

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

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

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


마치며

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

핵심 원칙:

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

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

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


관련 글


자주 묻는 질문 (FAQ)

Q. 이 내용을 실무에서 언제 쓰나요?

A. Everything about C++ Segmentation Fault : from basic concepts to practical applications.

Q. 선행으로 읽으면 좋은 글은?

A. 각 글 하단의 이전 글 또는 관련 글 링크를 따라가면 순서대로 배울 수 있습니다. C++ 시리즈 목차에서 전체 흐름을 확인할 수 있습니다.

Q. 더 깊이 공부하려면?

A. cppreference와 해당 라이브러리 공식 문서를 참고하세요. 글 말미의 참고 자료 링크도 활용하면 좋습니다.


이 글에서 다루는 키워드 (관련 검색어)

C++, segfault, 세그폴트, 크래시, 포인터, 메모리, 디버깅 등으로 검색하시면 이 글이 도움이 됩니다.