정적코드분석/코딩표준

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 에 규정되어 있습니다.

2 Comments

  1. 개발을 직접 해 보셨나요?
    C++은 모르겠으나, C에서 errno 사용하지 않고서는 개발이 불가능할 수도 있습니다. 물론 errno 쳐다볼 일 없이 프로그램을 완벽하게 한다면야 errno가 필요없겠지만, 완벽하게 짠다고 짜도 발생하는 이유 모르는 오동작을 잡는 데에는 errno없다면 정말 힘들어 집니다.
    신입사원 중 한 녀석이 자기자 짠 소스를 디버깅하는데, errno가 뭔지 모르더군요. (학교에서 프로그래밍을 전혀 배우지 않아서.) 처음엔 그냥 그런 아인 줄 알았는데, errno도 모르고 지금껏 디버깅 해 왔다는 사실을 알고 선배들이 모두 ‘준 천재’로 인정했던 일이 생각나네요.

    1. 안녕하세요, 현직 개발자이자 전직 C/C++ 코드 분석기 개발자 입니다.

      제목만 보시고 내용을 읽어보지 않으신 모양입니다.
      내용은 MISRA, JSF, CERT 등에서 errno에 대한 위험성을 알려주고, 바른 사용법을 안내해주고 있습니다.

      errno 쓰지 않고 개발해도 전혀 문제 없습니다. 도리어 빠른 개발 측면에서는 더 나을 수 있습니다.
      다만 errno을 사용하면 CERT, MISRA, JSF 룰에 다 걸리니까,
      자동차 개발 모듈, 전투기 관련 모듈, 보안 관련 모듈 및 해당 룰을 사용하는 곳에 코드를 납품할 일이 있다면
      골치아플 일이 생길지도 모르는거지요.

Leave a Reply

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