[C++] C++ 고수들에게 질문합니다. NULL 포인터 함수 참조는 항상 죽을까요?
class ConsoleHelper
{
public:
void PrintLine()
{
printf("-----");
}void SetValue(int a)
{
m_someValue = a;
}private:
int m_someValue;
};int _tmain(int argc, _TCHAR* argv[])
{
ConsoleHelper* pConsoleHelper = NULL;
pConsoleHelper->PrintLine();
return 0;
}
오늘 약간은 흥미로운 주제를 발견해서 여러분에게도 퀴즈를 하나 내겠습니다.
위의 코드는 당연히 컴파일시에 아무 문제가 없습니다.
그러나 런타임에서 어떠한 일이 발생할까요?
Access violation이 일어나서 프로그램이 죽을까요?
정답은 "정상적으로 실행된다" 입니다.
(사실 저렇게 싱겁게 죽어버리면 포스팅할 꺼리가 안되겠죠.. ;;;;)
여기서 많은 의문점이 생겨날 것 입니다.
'이때까지 그토록 삽질하며 무수히도 죽여왔던 방법인데 죽지 않는다니..' 라구요..
도대체 ConsoleHelper 클래스 객체를 생성하지도 않았는데 어떻게 멤버 함수를 참조할 수 있습니까!! 말이 됩니까!! 라는 반응도 나올 수 있겠군요
이유는 살펴보겠습니다.
C++에서는 함수가 고정된 주소를 호출하게 되어있기 때문입니다.
그럼 위의 코드를 살짝 수정해서 Access violation 을 일어나게 하는 방법은 뭘까요?
int _tmain(int argc, _TCHAR* argv[])
{
ConsoleHelper* pConsoleHelper = NULL;
pConsoleHelper->PrintLine();pConsoleHelper->SetValue(1);
return 0;
}
위의 코드에서 SetValue라는 또 다른 멤버 함수를 호출합니다.
짝짝짝! 드디어 프로그램이 죽었습니다.
그런데 이 함수는 PrintLine() 함수와 차이가 뭘까요? 음.. m_someValue 라는 멤버 변수에 접근하네요.
SetValue의 어셈블리 코드를 살펴보면 m_someValue라는 멤버 변수를 알기 위해 this를 참조하게 되는데,
당연히 객체가 할당되지 않았기 때문에
mov eax, dword ptr [this] 이 부분에서 eax 레지스터에는 0이 할당되고
mov ecx, dword ptr [a] 이 부분에서는 SerValue의 파라미터로 전달된 1이 할당되고
mov dword ptr [eax], ecx 바로 이부분에서 eax 가 포인터가 0인 값을 참조하여 Access violation 이 발생하게 됩니다.
즉, Class의 객체가 NULL이어서 프로그램이 비정상 종료되는 대부분이 바로 Class의 멤버 변수를 접근하면서 생기는 문제입니다.
(멤버함수를 실행해서가 아닙니다.)
그렇다면 여기서 C++ 고수분들이 태클거실지도 모르겠습니다.
멤버 변수에 접근하지 않았는데도 Access violation이 일어났다고...
그런 경우는 어떤 경우일까요.
함수의 주소가 바로 맵핑되지 않는 경우겠지요?
바로 virtual function 입니다.
자 그럼 virtual function으로 프로그램을 죽이기 위해서 코드를 살짝 바꾸어 보겠습니다.
class ConsoleHelper
{
public:
virtual void PrintLine() //기존의 PrintLine 함수를 virtual로 정의하였습니다.
{
printf("-----");
}void SetValue(int a)
{
m_someValue = a;
}private:
int m_someValue;
};int _tmain(int argc, _TCHAR* argv[])
{
ConsoleHelper* pConsoleHelper = NULL;
pConsoleHelper->PrintLine();return 0;
}
virtual function은 함수의 주소가 Runtime에 결정이 되기 때문에
위의 코드에서
mov eax,dword ptr [pConsoleHelper] 이 부분에서 eax 레지스터에 0이 할당되고
mov edx,dword ptr [eax] 이 부분에서 포인터 주소가 0을 참조해서 Access violation이 일어납니다.
이제 좀 궁금중이 풀리셨는지 모르겠네요.
이번기회에 NULL pointer 객체인데 왜 안죽냐를 가지고, 조금 심도있게 살펴본 기회였던것 같습니다.
그리고 어떻게 Access Violation이 일어나서 죽는지도 알아봤습니다.
어디까지나 이것은 C/C++ 언어적 특성입니다.
그리고 저런게 가능하다고 할 지라도 실제코드에서 저렇게 NULL 객체에 함수를 사용하시는 분은 없겠죠?
혹시라도 똑같은 객체에서 어떤 멤버함수는 실행되고 다른 멤버함수는 violation이 일어나는 경우 객체가 NULL인지 살펴보면 되겠군요. :)
그럼 참고로 C#은 어떨까요?
여러분이 일반적으로 알고계시는 멤버함수를 실행할 수 없습니다.
class ConsoleHelper
{
public void PrintLine()
{
Console.WriteLine("-----");
}
}class Program
{
static void Main(string[] args)
{
ConsoleHelper consoleHelper = null;
consoleHelper.PrintLine();
}
}
위와 같이 C#으로 동일한 내용의 코드를 작성하였습니다.
그리고 아래와 같이 디어셈을 하였습니다.
mov ecx,dword ptr [dbp-40h] 를 실행하고 ecx 레지스터에는 0이 할당되었습니다.
cmp dword ptr [ecx], ecx 를 실행하다 아래와 같이 Exceptioin이 나게 됩니다.
"프로그래밍 / TIP& Study" 분류의 다른 글
| [TIP] XP, Vista에서 CD/DVD롬이 보이지 않을 경우 (0) | 2009/08/05 |
| [C#] WinForm 에서 Docking 순서 변경하기 (0) | 2009/07/29 |
| [WPF] InkCanvas 사용하기 (0) | 2009/06/29 |
| [C#] Control Library 만들 때 TIP (0) | 2009/06/26 |
| [C#] 자연스럽게 Pen으로 그리기 (0) | 2009/06/23 |
| [WPF] Canvas의 Width, Height Binding (0) | 2009/06/23 |
| [C#] DateTime으로 7일 후는 어떻게? (0) | 2009/06/23 |
| [C#] string의 byte 길이 구하기 (0) | 2009/06/22 |






댓글을 달아 주세요