관리 메뉴

ㄴrㅎnㅂrㄹrㄱi

[강좌 20] 메모리 본문

API 관련/API 강좌모음

[강좌 20] 메모리

님투 2007. 10. 26. 03:43
반응형

[API]강좌(20)<--메모리






메모리에 대한것을 알아 보겠습니다.
우리는 앞으로 윈도우즈에서 할수 있는 메모리 할당 두가지 방법을 알아 볼 것
입니다.
먼저 첫번째 방법을 알아 보도록 하죠. 우리는 앞에서 이미 메모리 할당에 대해
알아 보았습니다. 기억하실지 모르겠지만 장치 독립적인 비트맵 파일 출력 부분에
서 메모리 할당이 나왔거든요. 기억납니까? 함수 이름이 잘 생각이 나지 않습니까?
바로 GlobalAllocPtr()이라는 함수 였습니다. 해제할때에는 GlobalFreePtr()이라는
함수를 사용했구요. 그런데 이 프로그램을 컴파일했을때 에러가 나는 분들도 있었
을 겁니다. 그렇죠? 뭐 정의 되지 않은 함수라고 그랬을 겁니다. 그것은 우리가
이제껏 사용한 API 함수들은 windows.h라는 헤더 파일에 포함된 것인데 위 두 함
수는 그곳에 선언된 것이 아니기 때문입니다. 그러면 어디에 선언 되어 있을까요?
바로 windowsx.h라는 헤더 파일에 선언되어 있습니다. 그렇다면 이 헤더 파일은
언제 사용하는 헤더 파일일지 궁금할것입니다. 우리가 일반적으로 앞에서
windows.h라는 헤더 파일외에 선언한 헤더 파일들은 전부 하나의 공통적인 특징을
가진 함수들을 사용하기 위해서 취한 행동들이었다는 기억할겁니다. windowsx.h
헤더 파일은 우리가 사용하는 API 함수들을 이용해서 매크로로 정의 해놓은 헤더
파일입니다. 뒤에 가서 배울 인터페이스 부분에서 이 매크로들을 많이 이용할테니
이 헤더파일을 나름대로 미리 보는 것도 괜찮을 겁니다.
그러면 이 헤더 파일에 메모리 할당과 해제가 어떻게 정의 되어 있는지 봅시다.
#define GetInstanceModule(hInstance) (HMODULE)(hInstance)
#define GlobalPtrHandle(lp) \
((HGLOBAL)GlobalHandle(lp))
#define GlobalLockPtr(lp) \
((BOOL)GlobalLock(GlobalPtrHandle(lp)))
#define GlobalUnlockPtr(lp) \
GlobalUnlock(GlobalPtrHandle(lp))
#define GlobalAllocPtr(flags, cb) \
(GlobalLock(GlobalAlloc((flags), (cb))))
#define GlobalReAllocPtr(lp, cbNew, flags) \
(GlobalUnlockPtr(lp), \
GlobalLock(GlobalReAlloc(GlobalPtrHandle(lp) , \
(cbNew), (flags))))
#define GlobalFreePtr(lp) \
(GlobalUnlockPtr(lp), (BOOL)GlobalFree(GlobalPtrHandle(lp)))
우리가 앞에서 사용한 함수가 정의 되어 있죠? 우리는 이번에 이 매크로를 이용하
지 않고 제공하는 API 함수를 이용해서 할것입니다. 아마 이 방법을 안 상태에서
위 매크로를 보시면 왜 그런지 금방 이해가 갈겁니다.
그러면 시작해 봅시다. 먼저 메모리 블럭을잡아줘야 합니다. 이때 아래 함수를
이용해서 하면 됩니다.
HGLOBAL GlobalAlloc(
UINT fuFlags,
DWORD cbBytes
);
첫번째 파라미터에는 지정된 예약어를 지정함으로써 메모리 할당 방법을 결정하는
것입니다. 두번째 파라미터에는 할당할 바이트 수를 지정해주면 됩니다. 아래는
첫번째 파라미터에 지정될수 있는 예약어입니다.
GMEM_FIXED 고정된 메모리를 할당합니다.
GMEM_MOVEABLE 이동이 가능한 메모리를 할당합니다.
GPTR GMEM_FIXED|GMEM_ZEROINIT
GHND GMEM_MOVEABLE|GMEM_ZEROINIT
GMEM_ZEROINIT 초기에 0으로 초기화합니다.
위 함수를 이용해서 메모리 블록을 잡으면 그 블록의 시작 주소를 얻는 작업이 필요
할겁니다. 그래야 우리가 포인터 변수로 선언한 변수에 그 시작주소를 넣어줄수 있
을테니까요. 이때 아래 함수를 이용해서 하면 됩니다.
LPVOID GlobalLock(
HGLOBAL hglbMem
);
첫번째 파라미터에 앞에서 배운 GlobalAlloc() 함수에 리턴되는 메모리 핸들을 지
정해 주면 됩니다.
자 이번에는 해제하는 방법을 알아 봅시다.
BOOL GlobalUnlock(
HGLOBAL hglbMem
);
메모리 할당시 이동가능하게 할당했다면 사용한후에 위 함수를 이용해서 해제 해
주어야 합니다.
HGLOBAL GlobalFree(
HGLOBAL hglbMem
);
할당한 메모리 블록을 위 함수로 이용해서 해제하면 됩니다.
그러면 두개의 함수를 더 알아 봅시다.
GGLOBAL GlobalReAlloc(
HGLOBAL hglbMem,
DWORD cbBytes,
UINT fuFlags
);
위 함수는 할당한 메모리 블록을 다시 잡을때 사용합니다. 첫번째 파라미터에 다시
잡을 메모리 핸들을 지정하면 되고 두번째 파라미터에 할당할 바이트 수를 지정하
면 됩니다. 마지막 파라미터에는 지정된 예약어를 지정하면 되구요. 이 예약어는
앞에서 배운 GlobalAlloc() 함수의 예약어를 지정해주면 됩니다.
할당된 메모리 블록의 크기를 얻는 함수도 있습니다.
DWORD GlobalSize(
HGLOBAL hglbMem
);
간단하죠? 파라미터로 할당된 메모리 핸들을 지정해 주면 됩니다.
자 그러면 간단한 프로그램을 만들어 봅시다.
#include <windows.h>
#include <string.h>
#include <stdio.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain
(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszArg, int nCmdShow)
{
static char szAppName[] = "Memory Example";
HWND hWnd;
MSG msg;
WNDCLASS WndClass;
WndClass.style = CS_HREDRAW|CS_VREDRAW;
WndClass.lpfnWndProc = WndProc;
WndClass.cbClsExtra = 0;
WndClass.cbWndExtra = 0;
WndClass.hInstance = hInstance;
WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
WndClass.hbrBackground = GetStockObject(WHITE_BRUSH);
WndClass.lpszMenuName = NULL;
WndClass.lpszClassName = szAppName;
if(!RegisterClass(&WndClass))
return FALSE;
hWnd = CreateWindow(
szAppName,
szAppName,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL
);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK
WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDChDC;
PAINTSTRUCT ps;
HGLOBAL hg;
static char *szStr;
static char szBuff[80];
switch(message)
{
case WM_CREATE :
hg = GlobalAlloc(GHND, 80);
szStr = (char *)GlobalLock(hg);
strcpy(szStr, "This is Memory Alloc Test");
sprintf(szBuff, "Memory Alloc Size : %d", GlobalSize(hg));
return 0;
case WM_PAINT :
hDC =BeginPaint(hWnd, &ps);
TextOut(hDC, 50, 100, szStr, strlen(szStr));
TextOut(hDC, 50, 120, szBuff, strlen(szBuff));
EndPaint(hWnd, &ps);
return 0;
case WM_DESTROY :
GlobalUnlock(hg);
GlobalFree(hg);
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
정말 간단한 프로그램이군요. 굳이 어려운 프로그램을 만들어서 설명드릴 필요는
없죠.
case WM_CREATE :
hg = GlobalAlloc(GHND, 80);
이동가능한 80바이트의 크기를 가진 메모리 블록을 생성하고 있습니다.
szStr = (char *)GlobalLock(hg);
메모리 블록의 시작 주소를 넣어주고 있습니다.
strcpy(szStr, "This is Memory Alloc Test");
sprintf(szBuff, "Memory Alloc Size : %d", GlobalSize(hg));
문자열을 복사해주고 GlobalSize() 함수를 이용해서 할당받은 메모리 블록의
크기를 얻고 있습니다.
return 0;
위에서 얻은 값들을 이용해서 화면에 출력하는 루틴은 WM_PAINT 메시지 부분에
있습니다.
case WM_DESTROY :
GlobalUnlock(hg);
GlobalFree(hg);
PostQuitMessage(0);
return 0;
윈도우가 종료될때 할당한 메모리 블록을 해제하는 과정을 보여주고 있습니다. 자
이해하기 쉽죠? 이제 앞에서 언급한 windowsx.h 헤더 파일의 메모리 매크로 부분을
다시 보시기 바랍니다. 어때요?
 
이번에는 다른 방법을 알아 보도록 하겠습니다. 우리가 앞에서 알아본 방법은
전역 힙을 이용해서 메모리 블록을 잡는 방법이였는데 이번에는 이 힙 영역을 생성
한후에 그 영역내에서 메모리 블록을 잡는 방법입니다.
즉 다시말하면 현재 프로그램(프로세스)에만 필요한 개인 힙을 생성하고 그 부분에
서 메모리를 할당하는 거죠. 이 힙을 생성해서 메모리를 할당하는 것은 클립보드에
서도 사용하는 것이니 잘 알아 두시기 바랍니다. 사실상 어떻게 보면 위에서 알아
본 방법과 큰 차이점은 없습니다. 단지 힙을 생성하는 과정만 추가된것 뿐입니다.
HANDLE HeapCreate(
DWORD flOptions,
DWORD dwInitialSize,
DWORD dwMaximumSize
);
위 함수를 이용해서 힙을 생성할수 있습니다. 첫번째 파라미터에는 보통 0을 지정
하면 됩니다. 두번째 파라미터에는 힙 크기를 지정하면 되고 세번째 파라미터에는
힙의 최대 크기를 지정하면 됩니다. 보통 세번째 파라미터에 0을 지정해서 최대 크
기를 사용 가능한 메모리까지로 지정할수 있습니다.
LPVOID HeapAlloc(
HANDLE hHeap,
DWORD dwFlags,
DWORD dwBytes
);
위 함수를 이용해서 힙에서 메모리 블록을 잡을수 있습니다. 첫번째 파라미터에는
HeapCreate() 함수로 얻은 핸들을 지정해 주면 되고 두번째 파라미터에는 지정된
예약어를 지정해주면 됩니다. 이때 지정될수 있는 예약어는 다음과 같습니다.
HEAP_GENERATE_EXCEPTIONS 할당이 실패하면 에러 코드를 리턴합니다.
HEAP_NO_SERIALIZE 힙이 공유되지 않습니다.
HEAP_ZERO_MEMORY 0으로 초기화합니다.
마지막 세번째 파라미터에는 메모리 블록 크기를 지정해 주면 됩니다.
자 여기까지가 할당하는 부분이고 그러면 해제하는 부분을 보도록 합시다.
BOOL HeapFree(
HANDLE hHeap,
DWORD dwFlags,
LPVOID lpMem
);
위 함수를 이용해서 할당한 메모리 블록을 해제할수 있습니다. 첫번째 파라미터에
는 힙 핸들을 지정해 주면 되고 두번째 파라미터에는 0을 세번째 파라미터에는
메모리 블록의 시작주소를 지정하면 됩니다.
BOOL HeapDestroy(
HANDLE hHeap
);
위 함수를 이용해서 힙을 완전히 없앨수 있습니다. HeapFree()함수를 사용하지 않
고 바로 위 HeapDestrory()함수만 이용해서 해제해도 됩니다.
이번에도 역시 두개의 함수에 대해 더 알아 보죠.
LPVOID HeapReAlloc(
HANDLE hHeap,
DWORD dwFlags,
LPVOID lpMem,
DWORD dwBytes
);
위 함수는 이미 잡혀있는 메모리 블록을 다시 잡아주는 역할을 합니다. 첫번째 파
라미터에 다시 잡을 힙핸들을 지정해 주면 되고 두번째 파라미터에는 HeapAlloc()
함수에서 지정될수 있는 예약어를 지정해 주면 됩니다. 세번째 파라미터에는 다시
잡을 메모리 블록의 시작 주소를 지정해 주면 되고 마지막 파라미터에는 메모리
블록의 크기를 지정해주면 됩니다.
DWORD HeapSize(
HANDLE hHeap,
DWORD dwFlags,
LPCVOID lpMem
);
위 함수를 이용해서 힙에서 할당한 메모리 블록의 크기를 얻을수 있습니다. 파라
미터는 굳이 설명 드릴 필요는 없을것 같군요.
자 그러면 앞에서 만들어본 프로그램을 이 힙을 생성해서 하는 방법으로 바꿔 봅
시다.
#include <windows.h>
#include <string.h>
#include <stdio.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain
(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszArg, int nCmdShow)
{
static char szAppName[] = "Memory Example";
HWND hWnd;
MSG msg;
WNDCLASS WndClass;
WndClass.style = CS_HREDRAW|CS_VREDRAW;
WndClass.lpfnWndProc = WndProc;
WndClass.cbClsExtra = 0;
WndClass.cbWndExtra = 0;
WndClass.hInstance = hInstance;
WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
WndClass.hbrBackground = GetStockObject(WHITE_BRUSH);
WndClass.lpszMenuName = NULL;
WndClass.lpszClassName = szAppName;
if(!RegisterClass(&WndClass))
return FALSE;
hWnd = CreateWindow(
szAppName,
szAppName,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL
);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK
WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hDC;
PAINTSTRUCT ps;
HANDLE hHeap;
static char *szStr;
static char szBuff[80];
switch(message)
{
case WM_CREATE :
hHeap = HeapCreate(0, 256, 0);
szStr = (char *)HeapAlloc(hHeap, HEAP_ZERO_MEMORY, 80);
strcpy(szStr, "This is Memory Alloc Test");
sprintf(szBuff, "Memory Alloc Size : %d", HeapSize(hHeap, 0,
szStr));
return 0;
case WM_PAINT :
hDC = BeginPaint(hWnd, &ps);
TextOut(hDC, 50, 100, szStr, strlen(szStr));
TextOut(hDC, 50, 120, szBuff, strlen(szBuff));
EndPaint(hWnd, &ps);
return 0;
case WM_DESTROY :
HeapFree(hHeap, 0, szStr);
HeapDestroy(hHeap);
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
특별하게 볼 부분은 없는것 같군요. 먼저 윈도우가 생성되는 부분을 봅시다.
case WM_CREATE :
hHeap = HeapCreate(0, 256, 0);
힙을 생성하고 있습니다. 그 크기는 256바이트가 되겠군요.
szStr = (char *)HeapAlloc(hHeap, HEAP_ZERO_MEMORY, 80);
생성한 힙에서 메모리 블록을 잡고 있습니다. 메모리 블록의 크기는 80바이트죠.
strcpy(szStr, "This is Memory Alloc Test");
sprintf(szBuff, "Memory Alloc Size : %d", HeapSize(hHeap, 0, szStr));
return 0;
윈도우가 종료되면 메모리 블록을 해제하는데 그 부분을 보도록 하죠.
case WM_DESTROY :
HeapFree(hHeap, 0, szStr);
HeapDestroy(hHeap);
PostQuitMessage(0);
return 0;
HeapFree()함수 구문은 생략해도 상관 없습니다.
여기까지 끝~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
반응형

'API 관련 > API 강좌모음' 카테고리의 다른 글

[강좌 21] 그래프나 수치를 나타내는 기호  (0) 2007.10.26
[강좌 19] 초기화 파일  (0) 2007.10.26
[강좌 18] 파일과 디렉토리  (0) 2007.10.26
[강좌 17] 폰트  (0) 2007.10.26
[강좌 16] 멀티미디어4  (0) 2007.10.26
Comments