*메모리 누수 잡기 ----------------------------------------------------------------------------------- #include <crtdbg.h> 를 추가한다. 코딩의 마지막 부분에 _CrtDumpMemoryLeaks(); 를 써준다.
F5를 눌러 컴파일을 하면 메모리 누수가 일어났을경우 메모리 누수가 일어난 번지(??)를 알려준다.
누수가 일어난 부분을 확인한 후 _CrtSetBreakAlloc(번지); 를 처음 부분에 써주고 F5를 눌러 컴파일 하면 그 부분을 찾아간다. -----------------------------------------------------------------------------------
*NULL값을 확인하는 방법? ----------------------------------------------------------------------------------- #include <assert.h>를 추가한다. 확인하고자 하는 변수(?)를 assert(변수); 이렇게 써준다 . 변수가 NULL값일경우 화면에 에러메시지를 표시해준다. -----------------------------------------------------------------------------------
Visual Studio 에서 개발할 경우 메모리 누수를 확인할려면 디버그 모드에서 F5 (Ctrl+F5 아님) 로 프로그램을 실행하면 되는건 아시죠? 이때 C++ 의 경우는 new 를 사용하고 메모리 반환을 하지 않는경우 해당 위치를 output 창에 보여줍니다. 예를 들어 아래의 코드로 메모리 할당후 어디에서도 반환하지 않았다고 가정해봅시다. char *pszTest = new char[100] ; F5 로 프로그램 실행후 종료하면 Debug 출력창에 아래와 같이 나옵니다. Detected memory leaks! Dumping objects -> D:ProjectMemoryTest.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:ProjectMemoryTest.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
윈도우즈 NT 나 윈도우즈 98 에서 시스템을 강제로 다운시키는 방법에 대해서 알아봅니다.
2.본문
필요한 경우가 거의 없겠지만(바이러스가 아니고서야. ^^;;), 시스템을 강제로 다운시키고자 하는 경우가 있을 수 있습니다. 이런 경우 아래의 예제에서 처럼 어셈블리 코드를 사용하는 방법과 Thread 를 와장창 만들어서 thread 러쉬(?)를 통해서 다운시키는 방법이 있습니다. 아래의 예제를 참고 하세요.
소수점이 있는 나눗셈 연산을 정수로 계산하여 계산 속도가 빠르고 정확한 숫자를 얻을 수 있는 방법에 대해서 알아보겠습니다.
2.본문
정수로 계산은 소수점 계산보다 연산 속도가 빠릅니다. 그리고 정확한 숫자값을 얻기 위해서는 나누기 전에 왼쪽으로 16-bit shift하고, 동등한 16-bit의 2진 숫자를 주어야 합니다. 이것은 나누기 전에 100,000으로 곱셈을 한 효과가 있습니다. 이렇게 함으로써 소수점을 계산하지 않아도 됩니다.
Explorer 에서 파일 아이콘을 더블클릭할 경우 파일의 확장명에 따라 연결되어 있는 프로그램이 동작하게 되어 있다. 하지만 연결되어 있는 프로그램이 없을 경우 "연결 프로그램 찾기" 다이얼로그가 화면에 나타난다.
[추가 정보]
파일 아이콘에 연결된 프로그램이 없을 경우 연결 프로그램을 지정하도록 "연결 프로그램 찾기" 다이얼로그를 프로그램적으로 띄우는 방법은 ShellExecuteEx API 를 Call 할 때 "Openas" 를 파라미터로 사용하면 된다.
우선 ShellExecuteEx를 Call하기 전에 FindExecutable API를 사용하여 파일의 프로그램 연결여부를 우선 알아보도록 한다. 만약 연결된 응용프로그램이 있을 경우 SHELLEXECUTEINFO structure에 "Open" 파라미터를 사용하고, 그렇지 않을 경우 "Openas"를 사용하면 된다. 이 파라미터에 의해 ShellExecuteEx 가 "연결 프로그램 찾기" 다이얼로그를 화면에 나타내게 된다.
다음은 위 내용에 대한 Sample code이다.
// Get the name of the file to be openend. CFileDialog dlg(TRUE); if (dlg.DoModal() == IDOK ) { CString strFile = dlg.GetPathName();
// See if there is an association for this file. char strExecutable[FILENAME_MAX]; int result = (int)FindExecutable((LPCTSTR)strFile, NULL, (LPTSTR)&strExecutable); if (result == 31) { // No Associated .EXE file AfxMessageBox("There is no association for the specified file type. The 'Open With...' dialog box will now be displayed."); SHELLEXECUTEINFO sei = {sizeof(sei), 0, m_hWnd, "Openas", (LPCSTR)strFile, NULL, NULL, SW_SHOWNORMAL, AfxGetApp()->m_hInstance};
// Invoke the "Open With..." dialog box before opening the selected file. ShellExecuteEx(&sei);
} else if (result >= 32 ) { // Found an Associated .EXE file AfxMessageBox("The file will now be opened with the associated program..."); SHELLEXECUTEINFO sei = {sizeof(sei), 0, m_hWnd, "Open", (LPCSTR)strFile, NULL, NULL, SW_SHOWNORMAL, AfxGetApp()->m_hInstance};
// Open the selected file. ShellExecuteEx(&sei);
} else { // Error condition. AfxMessageBox("Some other problem has caused 'FindExecutable' to fail."); } }
CDC* pDC = GetDC(); int nSize1 = 100; // 작은 글꼴에서 보여지는 글꼴 크기 int nSize2 = MulDiv(nSize1, 96, pDC->GetDeviceCaps(LOGPIXELSY)); // nSize1의 크기를 현재 디스플레이의 인치당 픽셀수로 재계산한다.
m_Font.CreatePointFont(nSize2, "굴림체", pDC); // 단순히 폰트명과 폰트크기로 생성할때
m_Button.SetFont(&m_Font);
이렇게 해서 앞에 올린 코드를 대신하면 제어판에서 설정한 큰글꼴, 작은글꼴의 값에 상관없이 일관된 크기의 폰트를 지정할 수 있습니다. 큰글꼴로 설정하면 글자가 잘리는 현상을 막을 수 있죠.
// Select the image into the appropriate dc CBitmap* pOldBitmapImage = dcImage.SelectObject(this);
// Create the mask bitmap CBitmap bitmapTrans; int nWidth = Width(); int nHeight = Height(); bitmapTrans.CreateBitmap(nWidth, nHeight, 1, 1, NULL);
// Select the mask bitmap into the appropriate dc CBitmap* pOldBitmapTrans = dcTrans.SelectObject(&bitmapTrans);
// Build mask based on transparent colour dcImage.SetBkColor(crColour); dcTrans.BitBlt(0, 0, nWidth, nHeight, &dcImage, 0, 0, SRCCOPY);
// Do the work - True Mask method - cool if not actual display BitBlt(hDC, x, y, nWidth, nHeight, dcImage.m_hDC , 0, 0, SRCINVERT); BitBlt(hDC, x, y, nWidth, nHeight, dcTrans.m_hDC , 0, 0, SRCAND); BitBlt(hDC, x, y, nWidth, nHeight, dcImage.m_hDC , 0, 0, SRCINVERT);
도스처럼 console 창에 텍스트만을 출력하는 console 프로그램과 흔히 쓰는 GUI를 가진 프로그램이 있죠.
Visual C++에서 프로젝트를 만들 때
Win32 Application : WinMain 함수에서 시작하게 되는 GUI 프로그램
Win32 Console Application : main 함수에서 시작하는 console 프로그램
이 두가지가 있습니다.
제목에 적은 것 같은 에러가 나는 분들은 대부분 GUI 프로그램을 만드시려고 하고 WinMain은 만드셨는데, 프로젝트를 만들 때 console application을 선택하신겁니다. 반대로 WinMain이 없다고 에러가 나오면 GUI 프로젝트를 선택하고서 main 함수를 작성하신 것이겠죠.
자, 그럼 어떻게 해결하느냐. 설정을 바꿔주면 됩니다. 프로젝트 세팅(Alt + F7)에서 Link탭을 누르면 제일 아래에 에디트 박스가 보이죠? 잘 보면 /subsystem:console 이라고 써있을 겁니다. 이걸 /subsystem:windows로 바꿔주세요. (또는 상황에 따라서 그 반대로 해줄 수도 있겠죠) 이 옵션의 역할은 링커에게 있어서 이 프로그램이 어떤 함수로부터 시작해야 하는지를 알려주는 겁니다. 이 옵션이 console이라면 main을, windows라면 WinMain을 찾아서 그 함수에서부터 프로그램이 시작하게 하는거죠.
2. ATL에서 컨트롤을 만들 때 이 에러가 날 수 있습니다.
디버그에서는 잘 컴파일이 되던게 릴리즈로 빌드하면 저 에러가 날 수 있습니다. ATL에서는 생성되는 코드의 크기를 최소화 하기 위해서 CRT (C RunTime) 함수의 일부를 제한하고 있기 때문이죠(정확히 말하자면 start up 코드를 넣지 않습니다). 그런데 프로젝트에서 특정 C 함수를 사용하면 ATL에서 제한한 CRT의 기능이 필요해지고 따라서 에러가 나는겁니다.
이 해결책도 간단합니다.
a. 프로젝트 세팅에서 predefined keyword를 찾아서 _ATL_MIN_CRT를 지워주세요.
b. 초기화가 필요한 CRT 함수를 사용하지 마세요. -_-; MSDN에 따르면 strcmp대신 lstrcmp를 사용하는 식으로 피해갈 수 있다고 합니다.
특정 Thread 나 Window 에 아무런 영향을 끼치지 않으면서 단순히 그 Thread 나 Window 가 살아있는지를 확인하고 싶을때 '친절한 메세지' 를 사용하시면 됩니다.
2.본문
MSDN 의 '색인' 에는 나와있지 않지만(MSDN 에서 특이하다 싶은것들은 일반적으로 '색인'에 없죠. ^^), 윈도우 메세지 종류에 WM_NULL 이라는 메세지가 존재합니다. MSDN 이나 다른 책에서 WM_NULL 메세지를 benign message 라고 소개하고 있습니다.
WinUser.H 파일에 아래와 같이 정의되어 있습니다.
#define WM_NULL 0x0000
이 메세지는 메세지 큐에 날라가긴 하지만, 어느 윈도우에서도 이 메세지를 처리하지 않기 때문에 그냥 메세지 큐에 들어가기만 하는 메세지입니다. 그럼 윈도우는 왜 이런 메세지를 만들어 놓은 걸까요? Debugging Application 에서 보면 이 메세지를 정지해있는 혹은 메세지큐가 idle 상태에 있는 thread 를 깨우기 위해서 ( 마치 술 먹기 전에 밥먹는 것처럼..) 사용하고 있습니다. 또한 해당 윈도우가 정상적으로 메세지를 받을 수 있는 상태인지 단순히 확인만 하고 싶을때 ( 이런 경우가 종종있는데, 어떤 메세지를 보내야 할지 난감할때가 있습니다.
3.예제
// 일반 메세지 보내는것과 같습니다. PostMessage(m_hwndTarget, WM_NULL, 0,0);
●Open System Interconnection(개방형 시스템)의 약자로 개방형 시스템과
상호접속을 위한 참조 모델
●ISO(International Organization for Standardization : 국제 표준화 기구)에서 1977년 통신기
능을 일곱 개의계층으로 분류하고 각 계층의 기능 정의에 적합한 표준화된 서비스 정의
와 프로토콜을 규정한 사양
(2) 목적
● 시스템간의 통신을 위한 표준 제공 ● 시스템간의 통신을 방해하는 기술적인 문제들을 제거 ● 단일 시스템의 내부 동작을 기술하여야 하는 노력을 없앨 수 있다. ● 시스템간의 정보교환을 하기위한 상호 접속점을 정의 ● 관련규격의 적합성을 조성하기 위한 공통적인 기반 구성
(3) 기본요소
●개방형 시스템(open system): OSI에서 규정하는 프로토콜에 따라 응용 프로세스(컴퓨터,통
신제어장치, 터미널 제어장치,터미널)간의 통신을 수행할 수
있도록 통신기능을 담당하는 시스템
●응용 실체/개체(application entity):응용 프로세스를 개방형 시스템상의 요소로 모델화한 것
●접속(connection) : 같은 계층의 개체 사이에 이용자의 정보를 교환하기 위한 논리적인 통신
회선
●물리매체(physical media) : 시스템간에 정보를 교환할 수 있도록 해주는 전기적인 통신
매체(통신회선,채널)
2. OSI 계층 구조
(1) Physical layer(물리 계층)
● 장치(device)들간의 물리적인 접속과 비트 정보를 다른 시스템으로 전송하는데 필요한 규칙을 정의.
● 비트 단위의 정보를 장치들 사이의 전송 매체를 통하여 전자기적 신호나 광신호로 전달하는
역할
기계적 = 시스템하고 주변장치 사이의 연결을 하는 사항들을 의미 전기적 = 신호의 전위 규격과 변화의 타이밍에 관한 거
(데이터 전송속도와 통신 거리를 결정) 기능적 = 각 신호에 의미를 부여해서 무엇을 할 것인가를 정의
(또는 수행되는 기능을 정의) 절차적 = 기능적 특성에 의해 데이타를 교환하기 위한 절차를 규정
(2) Data Link layer(데이터 링크 계층)
● 인접한 두 시스템을 연결하는 전송 링크 상에서 패킷을 안전하게 전송 것
● 기능 : 링크의 양단간(end-to-end)에 데이터 이송
링크의 확립과 절단
링크의 에러 검출
링크의 공유
투명한 데이터의 흐름
링크의 오류 회복과 통지
● 전송제어 절차
※ 기본형 데이터
OSI 참조 모델의 데이터링크 계층에 대한 규격으로, 단말장치 또는 호스트 컴퓨터 사이에
데이터 전송을 수행하는 표준 프로토콜 - 전송 제어 기능은 10개의 전송 제어문자를 사용하여 실행
댓글을 달아 주세요
좋은 자료네요~ 마침 malloc 으로 인한 메모리 누수에 대해서 조사하던 참이었는데~
그런데 제가 지금 작성하고 있는 프로그램에서는
파일명과 라인수가 나오지 않습니다.
다른 프로그램에서 테스트 했을 경우는 다 나왔는데 말이죠. 혹시 이런 경우는 보지 못하셨나요?
비밀댓글 입니다
뉴스를 위한 감사합니다…
정말 같지 않는 블로그!