정적코드분석/코딩표준

errno 을 사용하지 마세요

errno 란?

errno는 <errno.h>에 정의되어 있는 광역 변수거나 시스템에 따라서 매크로이기도 한 값입니다. 이 errno은 라이브러리 함수 실행 중 에러가 발생하면 어떠한 에러가 발생했는지 체크하고자 확인하는 용도로 사용됩니다. 표준 라이브러리 함수가 정상적으로 실행을 마쳤으면 이 값은 0이 되지만, 수행 중 비정상적인 상황이 발생하여 정상적으로 실행을 마치지 못했으면 0 이외의 값을 가지게 되죠. 이 값으로 해당 라이브러리가 어떤 에러가 발생했는지 알 수 있습니다. (참고 소스 : IBM developersWork®)

#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

const char *FILE_NAME = "/tmp/this_file_does_not_exist.yarly";

int main( int argc, char **argv )
{
int fd = 0;

printf( "Opening %s...\n", FILE_NAME );
fd = open( FILE_NAME, O_RDONLY, 0644 );
if( fd < 0 ) {
// Error, as expected.
perror( "Error opening file" );
printf( "Error opening file: %s\n", strerror( errno ) );
}

return EXIT_SUCCESS;
}

실행결과는 다음과 같습니다.

chrish@dhcp2 [507]$ ./Debug/demo
Opening /tmp/this_file_does_not_exist.yarly...
Error opening file: No such file or directory
Error opening file: No such file or directory

errno 는 비추!

거의 모든 C/C++ 코딩 표준은 errno에 대해서 언급하고 있습니다. 하지만, 모든 코딩 표준에서 이를 사용하라고 권장하지 않습니다. C 프로그래밍 언어의 경우 코딩표준 MISRA-C:2004에서는 다음과 같은 이유로 errno을 사용하지 말 것을 권고하고 있습니다.

1. errno는 C의 라이브러리로, 그 이론은 충분히 유용하나 실제로 표준 라이브러리에서는 어설프게 구현되어있다.
2. 에러를 처리하기 위해  errno에 의지하기보다, 함수를 호출하기 전에 입력값을 확인해보는 방법이 낫다.

C++의 경우는 어떨까요? Joint Strike Fighter Air Vehicle C++ Coding Standard(이하 JSF AV C++ Coding Standard)에서도 규칙 17번으로 errno을 사용하지 말라고 규정하고 있습니다. 왜일까요? 이에 대한 해답은 C++의 창시자인 Bjarne Stroustrup의 ‘ C++ Style and Technique FAQ’가 제시하고 있습니다.

Q : 예외를 사용하면 저에게 어떤 좋은점이 있을까요?
A : 에러 처리에 예외를 사용하는 것은 여러분의 코드를 더욱 간단하고, 깔끔하고 에러를 덜 놓치게 합니다.
Q : C의 “좋은 errno와 if문”에 무슨 문제가 있나요?
A : errno와 if문을 사용하는 것은 에러 처리 코드와 일반적인 코드가 마구 뒤엮일 수 있습니다. 이 방식대로 코드를 짜면, 여러분의 코드가 지저분해지고, 모든 에러를 다룬다고 보장하기 어려워집니다.(스파게티 코드와 테스트에서 ‘쥐구멍’을 생각해보세요!)

Bjarne Stroustrup은 errno을 사용하지 말라고 충고하고 있군요. 사실 C++로 코드를 작성할 때는 에러를 예외(Exception) 처리를 하는 것이 좋은 길입니다. 이 때문에 JSF AV C++ Coding Standard 에서도 errno를 사용하지 말라고 한 것이구요. 다만 JSF AV C++ Coding Standard에서는 MISRA-C와 같이 전면 금지를 시키지 않고 예외사항을 두었습니다. 다른 어플리케이션과 에러 상태에 대해서 마땅한 통신수단이 없을때는 사용할 수 있습니다. 예를 들어 써드 파티 수학 라이브러리가 어플리케이션의 언더플로우/오버플로우나 out-of-range/domain 등의 정보를 알려주기 위해 errno를 사용할 경우에는 어쩔수 없이 사용해야겠죠.

Bjarne Stroustrup 사진
errno 사용하지 마세요~ from Bjarne Stroustrup (사진 출처 : Bjarne Stroustrup 홈페이지)

Errno 사용하려면 제대로…

1. 직접 선언 금지

일부 C 프로그램 중에서 <errno.h>를 사용하지 않고 아래와 같이 직접 선언하는 경우가 종종 있습니다.

extern int errno;

절대 이렇게 하지 마세요. 최근 C 라이브러리에서는 동작하지 않는 방법입니다. 물론 아주 옛날 UNIX 시스템에서는 해당 라이브러리가 없을 수 있습니다. 그때 사용할 수는 있지만요.

2. 연속 사용 금지

errno 은 라이브러리의 “마지막” 에러 번호를 담고 있습니다. ?errno 값을 변경시키는 라이브러리 함수를 2번 이상 사용하면 errno은 맨 마지막에 사용한 함수의 에러 값을 가지고 있는 것이죠. 이 경우 이전 함수가 발생시킨 에러 값은 조용히 묻혀버립니다.

3. 사용전 초기화

프로그램 진입점(Entry Point, 쉬운 예 : main 함수)이 아닌 이상 errno의 값은 다른 값으로 변경되었을 가능성이 높습니다. errno 을 사용하기 전에 반드시 0 으로 초기화하고 사용하는 것이 좋습니다. 이는 보안 코딩 규칙인 CERT ERR30-C 에 규정되어 있습니다.

Leave a Reply

Leave a Reply

Your email address will not be published. Required fields are marked *