정적코드분석/코딩표준

Undefined Behavior 란 무엇인가?

question

‘Undefined Behavior’ 란 무엇인가?

*이전 블로그에서 이전한 글

일부 프로그래밍 언어 표준문서에는 ‘Undefined Behavior’ 혹은 ‘behavior is undefined’ 라는 단어가 많이 나옵니다. 우리말로 말하자면 ‘정의되지 않은 행동’ 정도가 되겠습니다. C의 표준문서가 가장 대표적인 예입니다. C99 표준 드래프트 버전을 보시죠(보기). 스펙의 많은 곳에서 ‘Undefined Behavior’라는 단어를 찾을 수 있습니다. 그런데 표준 위원회에서 표준을 정하면 되지 왜 ‘Undefined Behavior’로 남겨둘까요? 남겨줘서 무슨 좋은 일이 있을까요?

‘Undefined Behavior’의 좋은 점

Undefined Behavior 가 있어서 좋은 점은 딱 한 가지입니다. 컴파일러 구현을 쉽게 해준다는 점입니다. 컴파일러는 소스코드를 읽어들여서 컴퓨터에서 실행할 수 있는 기계어 코드로 변환시켜줍니다. 이때 컴파일러는 가장 효율적인 코드를 만들어 내야하는데, 언어의 스펙 중 ‘Undefined Behavior’ 이라 명시된 스펙은 특정 방법으로 구현하라고 강제하지 않기 때문에 컴파일러 개발사 입장에서는 자신들이 생각하기에, 그리고 자신들의 환경에서 가장 쉽고 효율적인 방법으로 구현하는 것이죠. 이는 C와 C++ 언어 설계자의 철학 때문입니다. Undefined Behavior 는 C 기반 언어에 존재하는데, C 언어의 설계자는 저수준 프로그래밍 언어의 효율성을 극대화하기를 원했기 때문입니다. 효율성을 증가시키기 위한 최적화는 OS,시스템(CPU, 메모리 등)에 따 다르고, 구현하는 컴파일러에 따라서도 다르기 때문에 컴파일러 구현 시 유연성을 준 것이죠.

반면, 자바와 같은 ‘안전한’ 언어는 Undefined Behavior 를 사용하지 않습니다. 자바는 효율성보다는 안전함을 추구했고 그 안전함을 위해서 기꺼이 성능을 희생시켰습니다.

‘Undefined Behavior’의 나쁜 점

따라서 Undefined Behavior 스펙은 컴파일러마다 다르게 구현할 수 있기 때문에 프로그래머가 어떤 일이 일어날 지 예측할 수 없습니다. 컴파일할 때 에러가 날 수도 있고, 혹은 실행 중에 에러가 발생해서 비정상적으로 종료할 수도 있습니다. 진짜 운이 좋으면 프로그래머가 의도한대로 수행되겠죠. 막연히 ‘이렇게 되겠지’ 라고 예측해서 프로그래밍하면 좋지 않은 결과를 가져올 수 있습니다.

question
출처 : Flickr ryanmilani (http://www.flickr.com/photos/51029297@N00/5275403364/)

아래 코드를 보시죠.

#include

int multiply2(int a)
{
    return a * 2;
}

int main (void)
{
    int n = 3;
    printf ("Result: %d %d \n", n++, multiply2(n));
    return 0;
}

위 코드를 GCC로 컴파일을 수행했습니다.(gcc version 4.4.5 (Ubuntu/Linaro 4.4.4-14ubuntu5))

$ gcc test.c -o test
$ ./test
Result: 3 6

위 코드를 Microsoft Visual Studio 2008로 컴파일 및 실행했습니다.(버전 : Microsoft (R) 32비트 C/C++ 최적화 컴파일러 버전 15.00.21022.08(80×86))

Result: 3 8

컴파일러에 따라 결과가 완전히 다르죠? 이 때문에 동일 소스로 이 기종간 교차 수행을 보장해야 하는 환경에서 문제가 발생할 가능성이 높습니다. 예를 들어 임베디드 리눅스용 소프트웨어을 개발한다면 가정해봅시다. 보통 임베디드 기기는 CPU성능이나 저장장치 용량이 작기 때문에 보통 임베디드 기기내부에서 소프트웨어를 개발하지 않습니다. PC에서 개발하고 임베디드 CPU용 컴파일러(예: Arm 컴파일러)를 사용해서 크로스 컴파일(Cross-compile)을 하는 것이 보통입니다. 이 경우 PC용 컴파일러에서 잘 동작하는 소프트웨어가 임베디드 환경 상에서  제대로 동작하지 않을 수 있기 때문에 반드시 ‘Undefined Behavior’ 로 구현된 부분을 될 수 있는 한 피해야 합니다.

CERT나 JSF, MISRA와 같은 각종 코딩 스탠다드 역시 이 ‘Undefined Behavior’를 최대한으로 줄이는데 많은 부분을 할애하고 있습니다.

Leave a Reply

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