디버깅이 어려운 이유는 간단하다.
사람들은 자기가 보고자 하는 것만 보기때문이다. 아는 것만 보려하기 때문이다. 코드의 에러를 예측할 때는 자기가 아는 만큼 안에서 예측을 하게 된다. 고로 경험과 지식, 그리고 이 것들을 지혜로 흡수한 사람은 당연히 디버깅을 잘한다.
결론 : 무식이 죄다. ㅋ
어제 런타임 에러가 난 버그가 포함된 코드이다. 분명히 delete [] buf; 이 구문에서 런타임 오류가 나는 걸 바로 잡아냈다. 처음엔 후~ 뻔하지 동적으로 할당한 메모리를 초과해서 내가 무슨 짓을 하였구나~. 그런데 난 절대로 14바이트 이상 초과하여 무슨 짓을 하지 않았다. 그런데 이놈의 코드가 계속 런타임 에러를 내는 것이었다. 20분 정도를 들여다 본 후에야 나는 어디가 문제인지 그제야 알아차렸다. 후 -_-
대부분 C/C++를 배운 사람들은 위의 코드를 보자마자 어디가 잘못되었는지 바로 알 것이다. 심지어 10분전에 new연산자에 대해 배운 C++ 초봉들도 어라 저거 맞는 코드인가? 라고 생각할 것이다.
1. BYTE* buf = new BYTE(14);
2. BYTE* buf = new BYTE[14];
그렇다. [] 대신에 ()를 써서 생긴 런.타.임(run-time) 오류이다. 컴파일 과정에서 전혀 무리가 없는 코드이다. 처음에 저 버그를 발견하고 나서 아니 이놈의 컴파일러가 왜 에러를 안내지? 라고 생각하며 컴파일러에게 분풀이를 하였다. 하지만 조금만 생각을 해봐도 "에러를 낼 수가 없군~" 이라는 결론이 난다. 당연한거지만 바로 생성자 때문이다.
즉 컴파일러는 1번 코드에는 BYTE만큼의 크기(1바이트)로 동적으로 메모리를 할당한 다음, ()을 보고 아~ 생성자를 호출하여야 겠군! 이러면서 1바이트의 메모리에 14라는 값을 넣어주고 그 주소값을 buf에 저장하였다. 2번 코드는 []를 보고 14바이트 만큼 동적으로 메모리를 할당하고, 첫번째 주소를 buf에 저장한 것이다.
1번 코드에서는 동적으로 할당된 메모리는 1바이트인데, 14바이트인줄 알고 이것저것 해댔으니 당연히 delete 할 때 에러가 날 수 밖에 ... 그런데 BYTE라고 해봐야 unsigned char인데 저건 분명히 C/C++의 기본 자료형이다. int, char 같은 놈들에게도 생성자가 있단 말인가? 테스트를 해보았다.
int a(5); 과연 이 코드가 C 컴파일러는 어떻게 처리하고, C++에서는 어떻게 처리할 것인가?
C와 C++ 컴파일러는 과연 어떻게 처리할 것인가. 얼마전에 소개한 http://www.codepad.org 에서 코드를 돌려보았다. 역시나~ 결과는 예상대로 나왔다. 링크를 따라가보면 C컴파일러는 에러를 내고, C++ 컴파일러는 정상적으로 컴파일을 하고 실행하여 5라는 결과를 보여준다.
C 코드 : http://codepad.org/g0nB7w2B
C++ 코드 : http://codepad.org/0FhQsQu2
결론 : C에 객체지향이라는 개념이 추가되면서 C++가 나왔고, 그러면서 생성자라는 개념이 생기면서 새로 생긴 문법이었다.
어처구니 없는 삽질을 하며 얻어낸 작은 지식이자 작은 재미이다. ㅎ