본문 바로가기
Programming/C, C++

메모리 누수없는 코드 만들기 C, C++

by Chan_찬 2011. 6. 23.
728x90

Visual Studio 에서 개발할 경우 메모리 누수를 확인할려면

디버그 모드에서 F5 (Ctrl+F5 아님) 로 프로그램을 실행하면 되는건 아시죠?

이때 C++ 의 경우는 new 를 사용하고 메모리 반환을 하지 않는경우 해당 위치를 output 창에 보여줍니다.

예를 들어 아래의 코드로 메모리 할당후 어디에서도 반환하지 않았다고 가정해봅시다.

             char *pszTest = new char[100] ;

F5 로 프로그램 실행후 종료하면 Debug 출력창에 아래와 같이 나옵니다.

Detected memory leaks!

Dumping objects ->

D:\Project\MemoryTest.cpp(60) : {73} normal block at 0x00374E58, 100 bytes long.

 Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD

Object dump complete.

 

F4 또는 위의 파일경로명을 더블클릭 하면 해당 위치 소스코드의 포커스를 이동해주기 때문에

메모리누수를 해결하는데 아주 편리합니다.

이렇게 가능한 이유는 비쥬얼스튜디오에서 프로젝트 생성시 자동으로 추가된 아래의 구문 때문입니다

 

#ifdef _DEBUG

#define new DEBUG_NEW

#undef THIS_FILE

static char THIS_FILE[] = __FILE__;

#endif

보시면 아시겠지만 new 라는 놈을 DEBUG_NEW 로 다시 정의하였습니다.

실제 디버그에선  new 를 사용해도, DEBUG_NEW 가 실행하게 되는것입니다.

그렇다면, DEBUG_NEW 의 정체가 뭘까요?

 

DEBUG_NEW 가 정의되어있는 소스코드를 가보겠습니다.

DEBUG_NEW <AFX.H> 에 정의되어 있고, 내용은 아래와 같습니다.

#define DEBUG_NEW new(THIS_FILE, __LINE__)

오호라~ new 가 연산자 오버로딩 되어 있는게 확인됩니다.

일반적으로 우리가 생각하는 new 는 인자로 할당할 크기만 주게 되어 있습니다.

하지만, 위의 구문을 보면 해당 위치의 파일명과 라인수를 지정하게 되어 있군요.

다시말해, new 를 사용할때마다 사용된 곳의 파일 경로와 해당 라인수를 메모리에 함께 등록하는 것입니다.

 

결국 해당 메모리가 제거되지 않았을땐, 메모리에 기록해둔 파일경로와 라인수를 참조해서 디버그 출력창에 뿌려주고

이를 식별자로 사용하여 이동하기 쉽게 해주고 있습니다.

여기까지는 비쥬얼 스튜디오가 자동으로 해주기 때문에 크게 신경쓸 일이 없었습니다.

하지만, 문제는 C 코드일 경우입니다.

비쥬얼스튜디오는 불행하게도 C 코드로 된 프로젝트를 생성하는 마법사가 없습니다.

그러다보니 자동으로 생성해주는 메모리 누수 매크로도 제공하질 않습니다.

실제 C 코드에서 사용하는 malloc 을 사용해서 메모리 해제 하지 않고 결과를 보겠습니다.

char *pszTest2 = (char*)malloc(100*sizeof(char)) ;

위코드를 수행후 종료하면 디버그출력창에 아래와 같이 나옵니다.

etected memory leaks!

Dumping objects ->

{73} normal block at 0x00374E58, 100 bytes long.

 Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD

Object dump complete.

도대체 어떻게 하라는건지

더블클릭해도 아무 반응도 없고, 어디서 메모리 해제가 일어났는지 아무런 정보도 주질 못하고 있습니다.

 

그렇다고 포기할순 없죠.

C++ new에 사용했던 방식대로 C malloc 에도 똑같이 해주면 될 것 같습니다.

malloc 할때 현재 파일경로와 라인수를 식별자로 함께 등록해주면 되겠군요.

그렇다면 이러한 매크로(DEBUG_NEW와 같은)를 직접 만들어야 할까요?

 

친절하게도 비쥬얼스튜디오 헤더파일에 미리 다 만들어 놨더군요.

우선 결과 코드를 보겠습니다.

#ifdef _DEBUG

#include <crtdbg.h>

#ifdef malloc

#undef malloc

#endif

#define malloc(s) (_malloc_dbg( s, _NORMAL_BLOCK, __FILE__, __LINE__ ))

#endif

 

new 를 사용할 때 처럼, malloc 을 다시 다른 함수를 사용하게 정의하였습니다.

위 코드를 C 소스코드의 상단에 위치시킵니다.

그런후 다시 아까의 코드를 수행후 종료하면 친절한 디버그 출력창으로 바뀐걸 볼 수 있습니다.

Detected memory leaks!

Dumping objects ->

D:\Project\MemoryTest.c (68) : {73} normal block at 0x00374E58, 100 bytes long.

 Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD

Object dump complete.

 

위 매크로를 보시면 _malloc_dgb 라는 매크로가 보입니다.

우린 그저 가져다 쓴것에 불과하군요.

<crtdbg.h> 를 열어보시면 메모리 관련 함수인  malloc, calloc, realloc, expand, free, msize 에 대한

디버그용 매크로가 정의되어 있는걸 보실 수 있습니다.

그렇다면 _malloc_dgb 에서 사용된 인자들의 의미가 과연 무얼까요?

s 는 실제 할당할 크기(사이즈) 입니다.

_NORMAL_BLOCK 는 시스템에서 메모리를 할당할 때 사용되는 블록의 기본 단위 크기 입니다.

 __FILE__ 은 현재 코드의 전체 경로명입니다.

__LINE__ 은 현재 코드의 수행중인 라인수 입니다.

다시,  crtdbg.h 를 보시면 free 라는 놈도 디버그용이 있습니다.

이왕 하는거 위에서 정의된 모든 메모리 관련 함수에 대해서 디버그용으로 변경해 봅시다.

#ifdef _DEBUG

#include <crtdbg.h>

           #ifdef malloc

           #undef malloc

           #endif

           #define malloc(s) (_malloc_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__ ))

           #ifdef calloc

           #undef calloc

           #endif

           #define calloc(c, s) (_calloc_dbg(c, s, _NORMAL_BLOCK, __FILE__, __LINE__ ))

           #ifdef realloc

           #undef realloc

           #endif

           #define realloc(p, s) (_realloc_dbg(p, s, _NORMAL_BLOCK, __FILE__, __LINE__ ))

           #ifdef _expand

           #undef _expand

           #endif

           #define _expand(p, s) (_expand_dbg(p, s, _NORMAL_BLOCK, __FILE__, __LINE__  ))

           #ifdef free

           #undef free

           #endif

           #define free(p) (_free_dbg(p, _NORMAL_BLOCK))

           #ifdef _msize

           #undef _msize

           #endif

           #define _msize(p) (_msize_dbg(p, _NORMAL_BLOCK))

#endif

 

이제 메모리누수 안녕~~~


헤더파일에 선언하는것은 별로 좋지 못합니다.
소스파일에서 모든 헤더파일을 인클루드 한 후에 본 매크로가 들어가야 합니다.
비쥬얼스튜디오에서도 프로젝트 생성해주고 굳이 cpp 마다 DEBUG_NEW 를 사용하게 해준 이유를 생각해 보시면 될것 같군요.
불편하시더라도 그게 가장 안전한 방법입니다.

긁어붙인곳을 c 나 cpp 파일로 옮겨서 해보세요.

출처 : http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=51&MAEULNo=20&no=7288

728x90
728x90
Buy me a coffeeBuy me a coffee

댓글