본문 바로가기
개발

간단한 프로그램에 벌써 3번째 비정상종료 발견 --;

by esstory 2007. 10. 26.

며칠 전에 배포한 티스토리용 댓글 알리미용 프로그램
2007/10/23 - [프로그램] - 티스토리 라이브 Ver. 0.1b

에서 벌써 3번째 비정상적으로 종료되는 문제를 발견해서 패치 했습니다.

너무 오랫동안 강호(프로그램 세계)를 떠나있었다고 위안을 삼고는 있지만, 요 작은 프로그램을 만들면서 비정상 종료와 같은 큰 문제를 3개나 일으키다니 유구무언입니다.

후배들의 코드를 맨날 코드 리뷰 한답시고, 문제를 찾는 제가 오히려 문제를 만드는 사람이란 것도 아이러니 하네요. 코딩 안 하는 게 회사를 돕는 일이겠습니다 ^^;

 

코딩 할 때 항상 같은 문제를 또 일으키는 경우가 많은데, 그럴 때를 대비해서라도 이번에 발생한 3가지 비정상 종료의 원인이 된 코드를 적어 보려고 합니다.

 

1.     STL::string 의 헷갈리는 생성자

첫 번째 Crash string 생성자를 잘못 사용해서 발생했습니다.

코드는 http://eslife.tistory.com/ 과 같은 주소를 입력 받아 맨 끝에 ‘/’ 가 있을 경우 이를 제거하는 간단한 코드였습니다. 문제가 된 코드는 이렇습니다.

string sURL = (LPCSTR)m_sBlogURL;

// 맨 끝에 / 가 있으면 제거

string::size_type idx = sURL.find_last_of("/");

if (idx == sURL.size() - 1)

{

           sURL = string(sURL, sURL.size() - 1);                 // 비정상종료 원인

           m_sBlogURL = sURL.c_str();

string sNakidURL = string(sURL, 7, sURL.npos);      // 비정상종료 발생

}


STL
string 에는 다음과 같이 문자열의 일부만을 가져가는 편리한 생성자가 있습니다.  

string s(string str, stridx)

string s(char* chars, chars_len)

문제는 비슷하게 생긴 이 두 생성자가 하는 일이 전혀 다릅니다.

첫 번째 생성자는 stridx 에서부터 문자열을 복사하고

두 번째 생성자는 문자열의 처음부터 chars_len 만큼 문자열을 복사합니다.

그래서 sURL http://eslife.tistory.com/가 들어 있을 경우

sURL = string(sURL, sURL.size() - 1);          → sURL = "/"

이 리턴 되고

sURL = string(sURL.c_str(), sURL.size() - 1);   → sURL = "http://eslife.tistory.com"

이 됩니다.

아 간단한 내용인데 설명하려니 기네요.

결론은 제가 string 의 생성자에 char* 형을 넘겼어야 하는데, string 객체를 잘못 넘겨서 예상과 다른 값을 리턴 받았고 이로 인해 프로그램이 죽었습니다. (장합니다.--)

 

2.     STL::string NULL 을 세팅 하면 죽는다 --;

어찌 보면 당연한 일입니다. 문제가 된 코드는 웹 문서를 파싱하면서 ATL W2A 매크로를 사용해서 BSTR 데이터를 string 에 할당하면서 발생했습니다.


CComBSTR bOutText;

hr = pElmtChild->get_innerText(&bOutText);

string sText = W2A(bOutText);  // 프로그램Crash!!


get_innerText
에서 오류가 났거나 해서 bOutText 에는 NULL 이 들어갔나 봅니다. NULL 값을 string 에 할당하면 바로 죽네요. 문자열이 NULL 로 끝나기 때문에 NULL 을 할당하더라도 얘교로라도 죽지 않아야 할 것 같기도 한데 우리 예민한 STL::string 은 바로 죽습니다.

string sText = NULL; // 수행 즉시 죽습니다.!

 

3.     리스트컨트롤 칼럼 헤더 타이틀 변경하다 비정상종료

이 부분은 좀 할말 없게 되었습니다. 정말이지 워낙 오래간만에 코딩하다보니, 윈도우 기본 확장 컨트롤인 리스트 컨트롤 사용법도 잊었나 봅니다.

원래 하려던 일은 리스트 컨트롤의 칼럼 타이틀 속성을 읽어 들여, 텍스트 값만 변경하는 거였습니다. 문제의 코드는 다음과 같습니다.


CListCtrl m_Result;

LVCOLUMN colInfo;

m_Result.GetColumn( 0, &colInfo );

// ... 중략

colInfo.pszText = "최근댓글";

m_Result.SetColumn(0, &colInfo);


위 코드는 죽을 때도 있고 안 죽을 때도 있습니다.  늘 죽지 않는 코드는 아래와 같이 구하려는 칼럼에 대한 MASK 와 문자열 배열을 넘겨주어야 합니다. (윈도우 API를 보면 모 하나 가져오기 참 복잡합니다. 상용컨트롤이나, 직접 만든 컨트롤들은 원하는 값을 구하기 쉬운데 반해 리스트나 트리 같은 확장 컨트롤들은 개발한지 몇 년 지나니 전혀 생각이 안납니다. --)


LVCOLUMN colInfo;

TCHAR sColText[160];

colInfo.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM;

colInfo.pszText = sColText;

colInfo.cchTextMax = 160;

m_Result.GetColumn( 0, &colInfo );

colInfo.pszText = "최근댓글";

m_Result.SetColumn(0, &colInfo);

 

 

 

3가지 문제 모두 공통적인 문제가 있네요

  • MSDN 을 제대로 확인하지 않고 코딩 했다.
  • 오래 전의 희미한 기억에 따라 날로 먹으려고 했다.
  • 함수의 리턴 값을 제대로 테스트 하지 않았다
  • 코드 검토를 전혀 하지 않았다.
     

반성하고, 자주 코딩 하는 수 밖에 없을 것 같습니다.

다른 얘기로 요즘 들어 “Programming Rubby” 를 읽고 있는데 언어가 참 마음에 듭니다.

“abc”.length

-1942.abs

와 같이 상수들도 객체로 인식되어 바로 객체의 함수를 부를 수도 있고 배열과 map [] {} 만으로 간단하게 정의해 버리네요.

세상은 참 모르는 것 천지입니다.

배울 건 많은데, 기존에 알고 있던 건 다 잊어 버리고 있고, 머리가 텅 빈 것 같습니다.

주말에, 텅 빈 머리에 가을 바람이라도 채우러 가야겠습니다. ^^;

 

 

Daum 블로거뉴스에서 이 포스트를 추천해주세요. [추천]


댓글