프로그램이 단지 하나의 값만 요구한다면 단순한 변수를 선언하는 것이 간단하다. 

하지만 배열이나 구조체, 문자열 같이 큰 데이터를 다룰 때에는 new를 사용하는 것이 더 효율적이다. 

프로그램이 컴파일시에 메모리를 차지하는 방식을 static binding이라고 하고 new 를 사용하여 프로그램 실행시에 메모리를 할당하는 방식을 dynamic binding이라 한다. 

 

Dynamic array

 

int main() {
	using namespace std;
	// 동적 배열의 선언 (dynamic array)
	// Darray의 값(주소)에는 배열의 첫번째 주소가 들어가있다
	int* Darray = new int[10]; 
	
	Darray[0] = 3;
	Darray[1] = 4;
	Darray[2] = 5;
	Darray[3] = 6;
	Darray[4] = 7;

	cout << "배열의 1번 값은 : " << Darray[1] << endl; 
	cout << "배열의 주소는 : " << Darray << endl; // 00000283BBECA4F0
	// 포인터 변수에 1을 더하면 그 포인터가 지시하는 데이터형의 바이트 수만큼 증가한다 
	Darray = Darray + 1; // 배열 포인터에서 +1 은 다음 원소를 가르키게 된다 
	cout << "이제는 배열의 0 번 값은 : " << Darray[0]; 
	cout << " 배열의 1번 값은 : " << Darray[1] << endl;
	cout << "배열의 주소는 : " << Darray << endl; // 00000283BBECA4F4  int형 배열이기 때문에 4가 증가됐다 

	Darray = Darray - 1; // 다시 시작 위치를 지시한다 
	cout << "배열의 주소는 : " << Darray << endl; // 00000283BBECA4F0

	// 동적 배열의 해제 
	delete [] Darray; // 동적 배열의 경우 반드시 []를 써줘야 배열 전체가 해제된다 


	return 0;
}

 

Dynamic Structure 

 

구조체의 경우 선언된 포인터 변수를 통해 맴버 변수에 접근 할 수 없다. 

포인터 변수는 해당 구조체의 주소값을 의미하기 때문이다 . 

그래서 c++에서는 포인터 변수에서 구조체의 맴버에 접근하기 위해 -> 기호를 사용한다

(*value).member 으로도 접근 가능하다 

int main() {

	struct Mystruct{
		string name;
		int price;
		bool type;
	};

	Mystruct* ps = new Mystruct; 

	ps->price = 1000; 
	ps->name = "12345678901234567890123";
	(*ps).name = "apple";
	ps->type = true; 

	cout << ps->price << endl;
	cout << ps->name<< endl;
	cout << ps->type << endl;

	cout << "*ps의 사이즈 : " << sizeof(*ps);
	cout << " price의 사이즈 : " << sizeof(ps->price);
	cout << " name의 사이즈 : " << sizeof(ps->name);
	cout << " type의 사이즈 : " << sizeof(ps->type);
	delete ps; 

	return 0;
}

 

new를 활용하여 메모리를 절약하는 방법 

new를 사용하지 않으면 선언되는 변수마다 80Byte의 공간이할당되고 프로그램의 종료까지 유지되지만 

다음과 같이 입력받은 값의 크기만큼의 공간을 새로 할당하고 해당 변수의 사용이 끝나면 delete로 반환하는 방식으로 메모리를 절약할 수 있다 

char* getname(void); 

int main() {
	char* name; 
	name = getname(); // name에는 새로운 길이의 배열의 시작 주소가 있음 

	cout << "name의 주소값 : " << (int*)name << endl;
	cout << "name의 값 : " << name << endl;
	delete name; // 메모리 해제 

	name = getname(); // 해제한 메모리를 다시 사용

	cout << "name의 주소값 : " << (int*)name << endl;
	cout << "name의 값 : " << name << endl;
	delete name; // 메모리 해제 

	return 0;
}

char* getname() { // 새 문자열을 가리키는 포인터  
	char temp[80]; // 임시 배열 - 해당 함수가 return하면 해당변수의 메모리는 반환된다 (automatic variable)
	cout << "이름을 입력하세요 : ";
	cin >> temp;
	char* pn = new char[strlen(temp) + 1]; // 배열을 입력한 크기만큼의 새로운 주소의 배열을 생성
	// strcpy 는 secure에 문제가 있어 사용 권장하지 않아서 strcpy_s를 사용
	// strcpy_s 는 두번째 인자로 변경할 사이즈를 정해줘야함 
	strcpy_s(pn, sizeof(*pn)*strlen(temp) + 1, temp);// 새로운 크기의 배열에 기존 temp의 값을 넣음 
	return pn; // 새로운 배열의 주소를 리턴
}

 

automatic storage, static storage, dynamic storage

  • C++에서는 데이터를 저장해 두기 위한 메모리를, 대입하는 방법에 따라  자동 공간(automatic storage), 정적 공간(static storage), 동적 공간(dynamic storage)로 구분한다
  • 정적 공간은 static 변수를 사용하거나 전역변수로 정의할 수 있다. 
  • 함수의 호출에 따라 사라지고 생기는 변수들은 자동공간(stack의 형태로)에 저장된다.
  • 동적 공간은 힙(heap)이라고도 한다
  • 동적 공간은 free store(자유 공간)이라 부르는 메모리 풀(memory pool)을 관리한다. 
  • 메모리 풀은 자동 변수와 정적변수가 사용하는 메모리와 분리되어 있다. 
  • stack의 경우  메모리상에서 데이터들이인접하여 존재하게 된다.
  • new와 delete를 사용하여 dynamic storage에 저장된 데이터를 관리를 해줘야한다 new로 생성한 메모리 주소에 값을 대입하고 delete를 하지않고 새롭게 new로 값을 대입하면 이전에 값은 메모리 어딘가에 떠돌게 되고 프로그램이 종료되지 않는이상 다시는 접근하기 어렵다. 이런것을 memory leak (메모리 누수)라고 한다   

 

+ Recent posts