RSS에 올라온 글들을 쭈욱 읽다가 재미난 제목을 보게 되었습니다. 바로 RainyNova님의 "VIsual C++ 7.1의 기괴한 버그"란 글입니다. 어떤 버그인지 궁금하지 않나요? 바로 아래 코드가 그 버그를 보여주는 코드입니다. VC++ .NET 2003 컴파일러에서 컴파일한 다음 실행시켜 보세요. assert 경고가 발생하는 것을 볼 수 있습니다. 2다음에 점을 하나 찍으면 에러는 발생하지 않죠. 과연 버그일까요?
정답은 버그가 아닙니다. 그렇다면 무엇입니까? 왜 assert가 경고를 띄울까요? 말도 안 되는 부동소수 함수들이 없다면서 말이죠. 그것은 원래 그렇게 디자인된 것 입니다. 음 이렇게 표현하니 조금 이상하죠. 버그와 디자인 이슈에 관한 차이를 말하는 것 입니다.
int main()
{
printf("%f", 2);
return 0;
}
통상적으로 버그라 함은 설계자의 의도대로 사용을 했으나 그 결과가 설계자가 의도한 대로 동작하지 않는 것을 말합니다. 즉, 3을 넣으면 4가 나온다고 설계자가 말했는데 내가 3을 넣었는데 5가 나왔단 말이죠. 이것이 버그입니다.
디자인 이슈라는건 설계자가 제약 사항을 명시했으나 사용자가 그것을 정확하게 이해하지 않고 사용하는 과정에서 생긴 불상사에 관한 내용입니다. 설계자는 단 한마디 그렇게 사용하면 안 된다. 라고 대답할 수 밖에 없는 성질의 것이죠. 여기엔 두 가지 정도의 답이 존재합니다. 디자인 자체가 누가 봐도 엉터리라면 디자인 자체가 변경 되어야 하고, 디자인 자체가 수긍 가능하다면 설계자의 의도대로 사용을 해야 하는 것이죠.
자 그럼 다시 원래 문제의 코드로 돌아가 봅시다. R6002가 발생하는 원인은 무엇일 까요? VC++ 컴파일러에 포함된 CRT의 경우 부동 소수가 사용되지 않을 경우에는 부동소수 관련 함수를 바이너리에 포함시키지 않습니다. CRT 소스 파일인 cmiscdat.c에 포함된 여섯 개의 함수가 그것입니다. 그런데 불행 이도 문제를 발생 시킨 소스 코드에는 부동 소수가 없죠. 그래서 컴파일러는 여섯 개의 함수를 포함시키지 않았습니다. 그런데 printf의 %f 지시자는 그 빠진 함수 중의 하나인 _cfltcvt를 사용하려고 합니다. 그래서 에러가 나는 것이죠.
자 그럼 이 디자인이 과연 누가 봐도 불합리한 디자인 일까요? 전 그렇지 않다고 봅니다. 그 함수들이 단지 1K를 차지하더라도 불필요하다면 제거되도록 만든 것은 합리적인 선택입니다. 아이러니하게도 메모리가 넘쳐나는 세상이지만 여전히 메모리는 부족합니다. 따라서 아껴서 나쁠 이유는 없습니다. 물론 이 아끼는 과정이 코드의 가독성을 해치고, 여러 의미를 가지는 변수를 사용하는 것과 같은 유지보수 비용을 높이는 것이라면 자제해야 합니다. 하지만 이건 다른 문제입니다. 단지 부동소수가 없는 코드라면 우리가 몇 바이트 더 아껴주겠다는 의도입니다. 그리고 실제로 위와 같은 코드가 나올 확률도 낮습니다. 부동소수 연산을 하는 코드에 부동소수가 한번도 존재하지 않을 확률은 매우 낮지 않을까요?
이 문제와 관련된 글들을 읽어보시면 아시겠지만 많은 분들이 CRT와 DLL에 대한 몇 가지 사소한 오해를 하고 계신 것 같습니다. 그것들에 대한 논의는 다음에 포스팅 하겠습니다.
P.S) 디자인 이슈는 주로 이념적인 논쟁의 성질이 짙기 때문에 답이 없는 경우가 많습니다. 스택이 아래로 자라냐 위로 자라냐? 리틀엔디안이냐 빅엔디안이냐? RISC냐 CISC냐? 뭐 그런 거죠. 이 문제에 대해서도 그 몇 KB 안 되는 코드 때문에 assert 경고를 봐야 하느냐? 복잡해 져야 하느냐? 라고 생각할 수도 있습니다.