Computer >> 컴퓨터 >  >> 프로그래밍 >> C 프로그래밍

C/C++ 코어 덤프(세그먼테이션 오류)의 원인과 해결 방법

이 글에서는 C/C++ 프로그래밍에서 자주 마주치는 코어 덤프(Core Dump), 즉 세그먼테이션 오류(Segmentation Fault)가 발생하는 원인을 예제 코드와 함께 살펴봅니다.

세그먼테이션 오류는 프로그램이 허용되지 않은 메모리 영역에 접근할 때 발생합니다. 대표적인 원인으로는 다음과 같은 경우가 있습니다.

  • 읽기 전용(read-only) 메모리 영역에 데이터를 쓰려고 시도할 때
  • 손상되었거나 유효하지 않은 메모리 주소에 접근하려고 할 때
  • 이미 해제(free)된 메모리를 다시 사용하려고 할 때

예제 1: 문자열 리터럴 수정

문자열 리터럴은 컴파일 타임에 읽기 전용 메모리 세그먼트(.rodata)에 저장됩니다. 따라서 포인터를 통해 리터럴의 내용을 변경하려 하면 세그먼테이션 오류가 발생합니다.

int main(){
    char *str;
    str = "GfG";
    *(str+1) = 'n';  // 읽기 전용 메모리에 쓰기 시도 → 오류 발생
    return 0;
}

위 코드에서 str은 문자열 리터럴 "GfG"의 시작 주소를 가리킵니다. *(str+1) = 'n'은 두 번째 문자 'f'를 'n'으로 바꾸려는 시도인데, 이 영역은 수정이 불가능한 읽기 전용 공간이므로 프로그램이 비정상 종료됩니다.

예제 2: 배열 인덱스 범위 초과 접근

C/C++에서는 배열 경계 검사를 컴파일러가 수행하지 않습니다. 따라서 선언된 크기를 벗어난 인덱스에 접근해도 컴파일은 성공하지만, 실행 시 정의되지 않은 동작(undefined behavior) 또는 세그먼테이션 오류로 이어질 수 있습니다.

#include <iostream>
using namespace std;
int main(){
    int arr[2];
    arr[3] = 10;  // 크기 2짜리 배열의 범위를 벗어난 접근
    return 0;
}

크기가 2인 배열에 인덱스 3으로 접근하는 것은 허용된 메모리 범위를 넘어서는 행위입니다. 운영체제나 메모리 배치에 따라 즉시 오류가 발생하지 않을 수도 있지만, 잠재적으로 심각한 버그의 원인이 됩니다.

예제 3: 해제된 메모리 접근 (댕글링 포인터)

free()로 이미 해제한 메모리에 다시 접근하는 것도 대표적인 오류 원인입니다. 이처럼 해제된 메모리를 여전히 가리키는 포인터를 댕글링 포인터(Dangling Pointer)라고 부릅니다.

#include <stdio.h>
#include <stdlib.h>
int main(void){
    int* p = malloc(8);
    *p = 100;
    free(p);   // 메모리 해제
    *p = 110;  // 해제된 메모리에 쓰기 시도 → 오류 발생
    return 0;
}

참고로 위 예제에서 사용된 <alloc.h>는 구식(Turbo C 계열) 헤더이며, 표준 환경에서는 <stdlib.h>를 사용해야 합니다.

실행 결과

위와 같은 코드를 실행하면 일반적으로 다음과 같은 결과가 출력되며 프로그램이 비정상적으로 종료됩니다.

Abnormal termination of program

정리 및 예방 팁

세그먼테이션 오류를 예방하려면 다음 사항을 항상 유의해야 합니다.

  • 문자열 리터럴은 char*가 아닌 const char*로 받아 실수로 수정하지 않도록 합니다.
  • 배열 접근 시 인덱스가 유효 범위 내에 있는지 반드시 확인합니다.
  • free() 호출 후에는 포인터에 NULL을 대입하여 댕글링 포인터를 방지합니다.

디버깅 도구인 GDB나 Valgrind를 활용하면 코어 덤프 파일을 분석해 오류가 발생한 정확한 위치를 추적할 수 있으므로, 적극적으로 활용하는 것이 좋습니다.