assert와 errno는 둘 다 오류 주변에서 보이지만 확인하는 질문과 쓰는 시점이 다릅니다.
숏컷 코드
assert: "이 상황은 원래 절대 일어나면 안 된다"errno: "실패는 했는데 왜 실패했지?"
assert(ptr != NULL);
FILE *fp = fopen(path, "r");
if (fp == NULL) {
perror("fopen");
}역할 구분
이 둘은 모두 실패 주변에서 보이지만 같은 문제를 다루지 않습니다.
assert: 코드 내부 전제가 깨졌는지 본다errno: 함수 호출 실패 원인이 무엇이었는지 본다
assert(ptr != NULL);
assert(index >= 0 && index < count);이건 방어적 런타임 복구 코드가 아니라, 프로그래머의 가정을 즉시 깨뜨리는 용도입니다.
그래서:
- 내부 불변식 점검에는 좋다
- 사용자 입력 오류 처리에는 맞지 않는다
NDEBUG빌드에서는 사라질 수 있다
실패 처리
FILE *fp = fopen("missing.txt", "r");
if (fp == NULL) {
perror("fopen");
}또는 직접 저장해서 메시지를 만들 수 있습니다.
int saved = errno;
fprintf(stderr, "[%d] %s\n", saved, strerror(saved));여기서 핵심은 "실패를 확인한 직후" 읽는 것입니다. errno를 의미 있게 설정하는 함수 실패 직후가 아니면 이전 값이 남아 있을 수 있고, 다른 함수 호출이 끼면 값이 바뀔 수 있습니다.
perror는 가장 빠른 출력용이고, strerror(saved)는 직접 메시지를 조합할 때 유용합니다.
선택 기준
- 절대 깨지면 안 되는 내부 가정:
assert - 파일 열기/메모리 할당/시스템 호출 실패 원인 출력:
errno기반 처리 - 사용자 입력/외부 실패 처리: 반환값과 에러 메시지
- 함수 실패 뒤 원인을 로그로 남긴다: 실패를 확인한 직후
errno를 저장
이 둘을 같은 카드에서 같이 보는 이유는 "둘 다 실패 상황 주변에서 보이지만 목적은 다르다"는 점을 구분하기 위해서입니다.
주의할 점
assert(open_file(path) != NULL)처럼 부작용이 있는 표현식을 넣는 습관은 좋지 않습니다. 릴리스 빌드에서 assert가 제거되면 그 호출 자체가 사라질 수 있기 때문입니다.
참고 링크
2 sources