C

[C] 예외처리, 오류처리

HHRR 2025. 6. 29. 13:32

1. 예외처리

c는 예외처리를 위한 기능이 없다.

c++의 try-catch나 JAVA의 throw/catch 구문과 같은 예외처리 메커니즘을 지원하지 않는다..

대신 어떻게 처리하냐? 함수의 반환값, 전역변수(errno), 조건검사(if문), assert 등을 이용해 오류를 처리하는 방식이 일반적이다.

FILE *fp = fopen("data.txt", "r");
if (fp == NULL) {
    perror("파일 열기 실패");
    exit(1);
}

 

우리 회사도 이런식으로 오류처리를 하는데... 오류를 더 잘 처리하는 방법을 알아보자

2. 나쁜 오류처리

1) 반환값을 무시하는 경우

// malloc 실패 여부를 확인하지 않음
char *buf = malloc(1024);
strcpy(buf, "hello");  // buf가 NULL일 경우 crash

 

2) 의미없는 에러코드

int read_data() {
    // 실패해도 -1이 아닌 0을 반환 (의미 불명확)
    return 0;
}

 

3) 오류 발생 위치와 원인을 숨김

void foo() {
    bar();  // bar가 실패해도 foo에서 처리하지 않음
}

 

-> 이거 제일 주의해야됨.... 이런식으로 처리돼서..  오류가 어디서 났는지 로그로 확인이 어려웠던 경우가 많았다!!

3.  버그와 오류의 차이

  • 오류(Error): 실행 중 발생할 수 있는 예측 가능한 문제이다.
    예: NULL 포인터, 파일 없음, 잘못된 입력 등. → 프로그램이 이를 감지하고 적절히 대응해야 한다.
  • 버그(Bug): 프로그래머의 실수로 인해 논리적 오류나 예상치 못한 결과가 발생하는 상황이다.
    예: 잘못된 조건문, 경계값 처리 실패 등. → 예외 처리로 막는 것이 아니라 코드 자체를 수정해야 한다.

4.  오류 처리 전략

1. 입력 유효성은 명시하고 검사해라

  • 모든 함수가 유효한 데이터를 받는다고 가정하지 말 것
  • 필요시 함수 이름이나 주석, 문서로 유효하지 않은 입력 가능성을 명시할 것
int parse_int(const char *str);  // str은 NULL일 수 있음

 

2. 반환값 또는 출력 인자에서 오류 정보를 명확히 표현하라

  • 성공/실패 여부를 int, bool, enum 으로 명확히 반환
int load_file(const char *path, char *buf, size_t size); // 성공시 0, 실패시 -1

 

 

3. assert() 는 내부 로직 검증용으로만 사용하라

  • assert() 함수란? 디버깅 모드에서 개발자가 오류가 생기면 치명적일 것이라고 생각하는 곳에 심어 놓는 에러 검출용 코드. 
  • 프로그램의 전제조건을 확인할 때 사용
assert(ptr != NULL);  // ptr은 절대 NULL이 아니어야 함

 

4. 중앙 집중식 오류처리 전략을 설계하자

  • 모든 함수에서 오류를 직접 처리하기 보다는, 상위레벨 함수에서 에러 코드를 받아 처리하는 방식이 유지보수에 유리하다. => 이부분 나중에 더 공부해보자. 프로프레임의 오류처리 방식도 이런 방식임
int ret = read_config();
if (ret != 0) {
    log_error("Config 읽기 실패");
    return EXIT_FAILURE;
}

 

5. 에러코드 체계화 및 문서화

  • enum 또는 #define을 사용하여 에러 코드를 구분하고 일관성 있게 정의한다.
#define ERR_FILE_NOT_FOUND -1
#define ERR_INVALID_PARAM  -2

 

 

 

c에는 예외처리 기능이 없어서, 명시적이고 일관된 오류 처리 전략이 필요하다.. 방어적 프로그래밍에 대해 다음에 더 알아보자