본문 바로가기

C++/Dynamic Memory

C++ Dynamic Memory

C++ 프로그래밍, 동적 메모리 할당 ( Dynamic Memory Allocations )


C 언어에서는 동적 메모리 할당을 위해 malloc, calloc, realloc, free 등의 함수가 <stdlib.h>에 선언되어 있는데, C++에서도 <cstdlib> 헤더를 포함하면 그대로 사용할 수도 있다. 그러나 C++에서는 새로 정의한 연산자 new, delete[] 를 이용하여 보다 쉽게 동일한 효과를 낼 수 있다.


배열의 크기를 동적으로 런타임에 결정해야 하는 문제

int main() 
{
     int size = 0;
     cin >> size;
     int arr[size];  // 런타임에 동적으로 배열의 메모리를 할당함
     return 0;
}


C언어 표준 C99에서는 위와 같이 배열의 메모리가 컴파일 타임이 아닌 런타임에 할당하는 것도 지원한다. 컴파일러에 따라 다를 수도 있기 때문에 위와 같은 코드가 C언어에서 오류를 발생할 수도 있다

C++에서는 컴파일러에 따라서 위의 방식을 지원하는 경우도 있는데, c++14 부터는 모든 C++에서 지원될 전망이라고 한다

필자의 개발환경은 Windows 8, Eclipse, MinGW이며, 컴파일러 스펙은 c++11인데, 위의 코드가 제대로 컴파일 및 실행되는 것을 확인할 수 있다.

만약 위와 같이 배열을 생성할 때 런타임에 배열의 크기를 결정하려는 부분에서 오류가 발생한다면 어쩔 수 없이 C++의 동적 메모리 할당(Dynamic Memory Allocation) 방법을 사용해야 한다


Eclipse, MinGW 환경에서 컴파일러 스펙 확인


다음은 C++에서 별다른 방법을 사용하지 않고도 동적으로 배열의 메모리를 할당하는 예이다

#include <iostream>
#include <locale>
#include <string>
#include <sstream>

using namespace std;

int main()
{
	setlocale(LC_ALL,"");

	wcout << L"동적 메모리 할당 ( Dynamic Memory Allocations )" << endl;
	wcout << L"다수개의 수를 입력할 때 공백으로 구분한 후에 <ENTER>를 누르세요:";

	wstring inStr;           // 키보드 입력을 와이드 문자열에 저장할 변수
	getline(wcin, inStr);    // 키보드에서 숫자를 다수개 공백으로 구분하여 입력
	wcout << inStr << endl;  // 입력된 문자열을 화면에 출력해본다

	// 이용자가 키보드에서 몇개의 숫자를 입력했는지 확인하여 cnt 변수에 갯수를 저장한다
	int cnt = 0;
	int pos = 0;
	while(true){
		int idx = inStr.find(L' ', pos);  // 공백문자 위치 확인
		if(idx==string::npos) break;  // string::npos == -1
		cnt++;
		pos = idx+1;
	}
	cnt += 1;

	wcout << L"입력한 숫자의 갯수:" << cnt << endl;

	// 배열을 선언하고 동적으로 메모리를 할당한다(실행시간에 배열의 메모리 크기를 결정한다)
	int num[cnt];

	int n = 0;

	// 와이드 문자열 스트림의 생성자에 키보드 입력 문자열을 전달하여 숫자를 추출한다
	// <sstream>
	wstringstream wstream(inStr); //와이드 문자열 스트림 객체 생성
	for(int i=0;i<cnt;i++){
		wstream >> n;             // 문자열 스트림으로부터 숫자를 하나씩 추출하여 변수에 저장
		num[i] = n;               // 숫자를 배열에 저장
		cout << "n=" << n << endl;
	}

	// 배열에 저장된 숫자를 화면에 출력하여 확인한다
	for(int i=0;i<cnt;i++) {
		cout << "num[" << i << "]=" << num[i] << endl;
	}

    return 0;
}


위와 같은 방법으로 배열을 사용할 수 없는 환경에서는 C++에서 지원하는 동적 메모리 할당 연산자(new)를 이용하여 해결해야 한다.

동적 메모리를 요청할 때는 new 자료형[갯수]; 형식을 사용하고 사용한 동적 메모리를 해제할 때는 delete[] 연산자를 사용하면 된다

#include <iostream>
#include <locale>
#include <string>
#include <sstream>

using namespace std;

int main()
{
	setlocale(LC_ALL,"");

	wcout << L"동적 메모리 할당 ( Dynamic Memory Allocations )" << endl;
	wcout << L"다수개의 수를 입력할 때 공백으로 구분한 후에 <ENTER>를 누르세요:";

	wstring inStr;           // 키보드 입력을 와이드 문자열에 저장할 변수
	getline(wcin, inStr);    // 키보드에서 숫자를 다수개 공백으로 구분하여 입력
	wcout << inStr << endl;  // 입력된 문자열을 화면에 출력해본다

	// 이용자가 키보드에서 몇개의 숫자를 입력했는지 확인하여 cnt 변수에 갯수를 저장한다
	int cnt = 0;
	int pos = 0;
	while(true){
		int idx = inStr.find(L' ', pos);  // 공백문자 위치 확인
		if(idx==string::npos) break;  // string::npos == -1
		cnt++;
		pos = idx+1;
	}
	cnt += 1;

	wcout << L"입력한 숫자의 갯수:" << cnt << endl;

	// 배열을 선언하고 동적으로 메모리를 할당한다(실행시간에 배열의 메모리 크기를 결정한다)
	//int num[cnt]; // 컴파일러가 왼쪽과 같은 동적할당 방식을 지원하지 않으면 아래처럼 하면 된다
	int * num = new int[cnt];  // 위의 문장 대신에 new 연산자를 사용한 경우

	int n = 0;

	// 와이드 문자열 스트림의 생성자에 키보드 입력 문자열을 전달하여 숫자를 추출한다
	// <sstream>
	wstringstream wstream(inStr); //와이드 문자열 스트림 객체 생성
	for(int i=0;i<cnt;i++){
		wstream >> n;             // 문자열 스트림으로부터 숫자를 하나씩 추출하여 변수에 저장
		num[i] = n;               // 숫자를 배열에 저장
		cout << "n=" << n << endl;
	}

	// 배열에 저장된 숫자를 화면에 출력하여 확인한다
	for(int i=0;i<cnt;i++) {
		cout << "num[" << i << "]=" << num[i] << endl;
	}

	delete[] num;	// 동적 메모리를 사용한 후에는 해제해 주어야 한다

    return 0;
}


new 연산자를 사용한 동적 메모리 할당시 발생가능한 오류 처리 2가지 방법

new 연산자를 사용하여 메로리를 동적으로 할당할 때 만약 오류가 발생한다면 2가지 방법을 이용하여 오류에 대응할 수 있다. 만약 오류가 발생한다면 프로그램은 그 위치에서 비정상 종료하게 된다. 그러므로 오류를 처리하는 코드를 추가할 필요가 있는데, 이는 나중에 다룰 예정이다. 위의 코드에서는 아무런 예외처리도 하지 않았기 때문에 오류가 발생하면 그대로 비정상 종료하게 된다

그러나 오류가 발생하더라도 프로그램이 비정상 종료하지 않고 new 연산자가 NULL을 리턴하고 프로그램은 그대로 진행되는 방식도 사용할 수가 있다. 이 방법은 오류를 던지지 않는다는 의미로 (nothrow) 키워드를 사용하여 처리한다


new 연산자를 이용하여 동적 메모리 할당시 발생할 수 있는 오류를 (nothrow) 방식으로 처리한 예

#include <iostream>
#include <locale>
#include <string>
#include <sstream>
#include <cstdlib>  // exit()

using namespace std;

int main()
{
	setlocale(LC_ALL,"");

	wcout << L"동적 메모리 할당 ( Dynamic Memory Allocations )" << endl;
	wcout << L"다수개의 수를 입력할 때 공백으로 구분한 후에 <ENTER>를 누르세요:";

	wstring inStr;           // 키보드 입력을 와이드 문자열에 저장할 변수
	getline(wcin, inStr);    // 키보드에서 숫자를 다수개 공백으로 구분하여 입력
	wcout << inStr << endl;  // 입력된 문자열을 화면에 출력해본다

	// 이용자가 키보드에서 몇개의 숫자를 입력했는지 확인하여 cnt 변수에 갯수를 저장한다
	int cnt = 0;
	int pos = 0;
	while(true){
		int idx = inStr.find(L' ', pos);  // 공백문자 위치 확인
		if(idx==string::npos) break;  // string::npos == -1
		cnt++;
		pos = idx+1;
	}
	cnt += 1;

	wcout << L"입력한 숫자의 갯수:" << cnt << endl;

	// 배열을 선언하고 동적으로 메모리를 할당한다(실행시간에 배열의 메모리 크기를 결정한다)
	//int num[cnt]; // 컴파일러가 왼쪽과 같은 동적할당 방식을 지원하지 않으면 아래처럼 하면 된다
	int * num = new(nothrow) int[cnt];  // 위의 문장 대신에 new 연산자를 사용한 경우
	// 위에서 (nothrow) 를 사용하여 오류를 던지지 않도록 했기 때문에 만약 오류가 발생하면 NULL 이 리턴된다

	if(num==NULL) {  // 동적 메모리 할당시 오류가 발생했는지 확인한다
		wcout << "동적 메모리 할당시 오류가 발생했습니다" << endl;
		exit(1);
	}

	int n = 0;

	// 와이드 문자열 스트림의 생성자에 키보드 입력 문자열을 전달하여 숫자를 추출한다
	// <sstream>
	wstringstream wstream(inStr); //와이드 문자열 스트림 객체 생성
	for(int i=0;i<cnt;i++){
		wstream >> n;             // 문자열 스트림으로부터 숫자를 하나씩 추출하여 변수에 저장
		num[i] = n;               // 숫자를 배열에 저장
		cout << "n=" << n << endl;
	}

	// 배열에 저장된 숫자를 화면에 출력하여 확인한다
	for(int i=0;i<cnt;i++) {
		cout << "num[" << i << "]=" << num[i] << endl;
	}

	delete[] num;	// 동적 메모리를 사용한 후에는 해제해 주어야 한다

    return 0;
}