new가 실패했을때의 처리

데브피아 김용현님의 글

 

보통 프로그래밍을 할때 new에 대한 실패 처리는 해주지 않고 코딩을 합니다.

 

문자열이나 new 를 쓰지 않는 어플리케이션은 없다고 해도 틀린말은 아니고 다음과 같은 코드는 일반적으로 많이 쓰이는 코드 입니다.

char* szName = new char[128];
new 에 대한 에러 처리는 하지 않습니다. 만일, 아주 큰 new 를 많이 해야 할 경우라던지, 특수한 환경의 임베디드라면, 가끔 new 에 대한 방어코딩을 하고 싶을때가 있습니다. 그럴때 대부분 다음과 같은 코드를 작성합니다.

char* szName = new char[128]; 
if(!szName)
    NewFail();

 

그러나 위와 같은 코드는 옳지 않고 동작하지 않습니다. 테스트를 할때에도 new가 할당 실패하는경우는 거의 없으므로 프로그래머는 위와 같은 코드가 동작하지 않는다는것에 대한 피드백을 받지 못합니다.

 

앞으로 내용에서 위와 같은 코드가 동작하지 않는것을 확인하고, 어떠한 방식으로 new의 실패를 처리해야 하는지 알아 보겠습니다.

 

먼저 테스트 코드를 아래와 같이 작성하였습니다. 컴파일을 최적화 옵션을 끈 상태에서 release 모드로 컴파일후 컴파일러를 종료하고,

 

OutputDebugString 을 받을수 있는 디버거를 켠후 실행하였습니다.(펜4 3.0, 512램, 최적화옵션을끈 release 모드를 콘솔에서 직접실행)

#include <Windows.h> 
int main()
{
    int* _p = 0;
    while(1) 
    { 
        _p = new int[0xFFFFFF];   
        if(_p == 0) 
        { 
            OutputDebugString("zero"); 
        } 
        else 
        { 
            OutputDebugString("Succeed\n"); 
        } 
    }; 
}

 

new에 try – catch 를 사주고 다시 실행을 시키겠습니다. 아래 코드를 같은 환경에서 실행시켰습니다.

include <Windows.h> 
int main()
{
    int* _p = 0;
    try 
    { 
        while(1) 
        { 
            _p = new int[0xFFFFFF];   
            if(_p == 0) 
            { 
                OutputDebugString("zero"); 
            } 
            else 
            { 
                OutputDebugString("Succeed\n"); 
            } 
        }; 
    }
    catch(...) 
    { 
        OutputDebugString("catch"); 
    } 
}

 

catch 가 나오는것을 알 수 있습니다. new 의 실패로 익셉션이 발생하였고, 익셉션을 처리할 코드가 없으므로 프로그램이 비정상 종료가 된것입니다.

 

new에 할당을 실패하면 bad_alloc 익셉션이 발생하는 다음과 같은 코드가 실행됩니다.

_CRTIMP2 void __cdecl _Nomemory() 
{ 
    /* report out of memory */ 
    static const _XSTD bad_alloc nomem; 
    _RAISE(nomem); 
}

 

따라서 new 에 대한 할당 실패를 처리 해줄때, 단순히 0을 체크 하는것으로는 안됩니다.

 

방법1 set_new_handler나 try-catch를 통하여 익셉션 핸들러를 만들어 준다.

 

방법2 new에 할당을 실패하게 되면 컴파일러는 0을 리턴하면서 동시에 익셉션을 발생합니다. 익셉션을 처리해 주던가 아니면, 익셉션을 무시하고 0을 처리하는 방법을 취해야 합니다.

char* szName = new (nothrow) char[128]; /* nothrow 를 명시하여 익셉션을 발생하지 않음. */ 
if(!szName)    /* 실패에 대한 처리를 해줌. */ 
    NewFail();

답변글 제가 알기로는 과거에는, null을 값을 반환하는 것이 표준이었다고 들었습니다. 그래서 사람들이 아직도 그런 방식으로 많이 사용하고 있는 걸로 들었다는..

 

아뭏튼, new에 대한 exception handling하는 방법 중의 하나는..set_new_handler 함수를 통하여, 그러한 exception이 발생했을 때 처리하는 함수를 설정해 주는 방법도 있고, 혹은 new 를 사용할 때 (nothrow) 옵션을 줘서, 리턴 값을 확인해서 처리하는 방법도 있습니다. nothrow 옵션을 준 경우는, 메모리가 없는 상황에서는 당연히 NULL 값을 리턴합니다.

 

예를 들어서…set_new_handler는..
void no_memory_exit()
{
cout << "no memory, program will be terminated!" <<endl;
abort()
}

..
set_new_handler(no_memory_exit);
int *p = new int[100000000];
이런 식으로, try~catch를 쓰지 않고서, exception을 핸들링할 수 있게 됩니다.

 

두번째 방법은..

 

int *pnew = new (nothrow) int?1000000;

 

이런 식으로 사용하면, 메모리가 없는 경우, 단지 NULL을 리턴합니다. 좀 더 자세한 이야기는 MSDN을 참고하시기 바랍니다.

 

그냥 김용현님의 이야기에 덧붙여서, 들어주시면 좋을 것 같네요..

OutputDebugString 으로 zero 가 나오는 것을 기대하고 프로그램을 실행시키지만, zero 는 나오지 않고 프로그램이 종료 되는것을 보실수 있습니다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 항목은 *(으)로 표시합니다