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