이번 포스팅에서 우리가 배워볼 것은 다음과 같다.
- 메모리 구성과 영역별로 저장되는 데이터의 유형
- 동적 할당 메모리
- 동적 메모리 사용 절차
- C언어 : 동적 메모리 할당 연산자 malloc, free
- C언어 : calloc 함수
- C++ : 동적 메모리 할당 - new, delete
- C++ : 스마트 포인터
- 객체의 동적 생성
1. 메모리 구성과 영역별로 저장되는 데이터의 유형
우리 컴퓨터에 있는 메모리에는 다음과 같이 구성되어 있다.
- 코드 영역 : 실행할 프로그램의 코드가 저장되는 메모리 공간.
- 힙 영역 : 동적 메모리 할당하는 장소.(이번 동적 할당 파트에서 중요한 부분이다)
- 스택 영역 : 함수 수행시에 메모리 할당이 일어나고 함수를 빠져 나가면 소멸되는 영역
- 데이터 영역 : 전역변수와 static 변수가 할당되는 영역. 프로그램 시작과 동시에 할당되어 종료시까지 남아있음.
2. 동적 할당 메모리
- 동적 메모리 할당(dynamic memory allocation)이란 프로그램이 실행 도중에 동적으로 메모리를 할당 받는 것을 말한다.
- 아직 사용하지 않는 메모리 공간인 힙(heap)에서 동적 메모리를 할당받는다.
3. 동적 메모리 사용 절차
- 동적 할당 메모리가 필요하게 된다면 그 필요할 때에 필요한 만큼만 할당 받을 수 있다.
- => 메모리를 효율적으로 사용 가능
- [동적 할당을 하게 된다면] 얼마나 할당을 받을 것인지를 결정하고 라이브러리 함수를 호출하여 운영체제에게 메모리를 요청한다.
- => 이 때 충분한 메모리가 존재하면 운영체제로부터 들어온 요청을 승인하고 메모리를 할당해준다.
- [프로그램 사용이 끝나면] 메모리를 다시 운영체제에게 반납한다.
- => 이 때 만약 메모리를 반납하지 않으면 다른 프로그램이 동적 메모리를 사용할 수 없게 되니 꼭 반납하자.
4. C언어 : 동적 메모리 할당 연산자 malloc, free
들어가기 전, 우리가 알아야 할 것이 있다. 먼저 C언어에서 동적 메모리 접근은 포인터를 통해서만 이루어진다. 또한 컴파일 시에 할당에 필요한 메모리 공간이 계산되지 않고, 실행 시에 할당에 필요한 메모리 공간이 계산되기 때문에 우리가 '동적할당'이라고 부른다.
메모리를 동적으로 할당하기 위해서는 malloc, free를 사용한다.
#include <stdlib.h>
void * malloc(size_t size); // 힙 영역으로의 메모리 공간 할당
void free(void * ptr); // 힙 영역에 할당된 메모리 공간 해제
cf) #include <stdlib.h> 가 가지고 있는 함수와 매크로
- 메모리 관리 함수: malloc(), calloc(), realloc(), free()와 같은 함수들을 제공하여 동적 메모리 할당과 해제를 관리한다.
- 난수 생성 함수: rand(), srand() 등을 통해 난수를 생성하고 시드 값을 설정할 수 있다.
- 문자열 변환 함수: atoi(), atof() 등을 통해 문자열을 정수나 부동 소수점으로 변환한다.
- 시스템 관련 함수: exit(), system() 등을 통해 프로그램을 종료하거나 시스템 명령을 실행한다.
malloc 함수는 성공 시 할당된 메모리의 주소 값을 반환하고, 실패 시 NULL 값을 반환한다.
다음은 malloc 함수와 free 함수의 사용 예시이다.
int main(void){
void * ptr1 = malloc(4); // 4바이트가 힙 영역에 할당
void * ptr2 = malloc(12); // 12바이트가 힙 영역에 할당
...
free(ptr1); // ptr1이 가리키는 4바이트 메모리 공간 해제
fre(ptr2); // ptr2가 가리키는 12바이트 메모리 공간 해제
...
// malloc 함수의 일반적인 호출형태 // sizeof 연산 이후 실질적인 malloc의 호출
void * ptr3 = malloc(sizeof(int)); // void * ptr3 = malloc(4);
void * ptr4 = malloc(sizeof(double)); // void * ptr4 = malloc(8);
void * ptr5 = malloc(sizeof((int) * 7); // void * ptr5 = malloc(28);
void * ptr6 = malloc(sizeof((double) * 9); // void * ptr6 = malloc(72);
}
malloc 함수는 인자로 숫자만 하나 전달받을 뿐이니 할당하는 메모리의 용도를 알지 못하기 때문에 메모리의 포인터 형을 결정짓지 못환다. 따라서 아래와 같이 형 변환의 과정을 거쳐서 할당된 메모리의 주소 값을 저장해야 한다.
// int 하나를 담을 수 있는 크기의 메모리 공간을 할당한다.
int *p = (int *)malloc(sizeof(int));
// 메모리에 값을 넣는다.
*p = 20;
// 사용이 끝난 메모리를 해제한다.
free(p);
C : 동적 메모리 할당의 규칙
다른 건 다 필요없고 fopen, fclose가 늘 쌍을 이루듯이 malloc과 free 쌍을 맞춰서 사용하자.
- free 함수를 호출하지 않으면?
- 할당된 메모리 공간은 메모리라는 중요한 resource를 계속 차지하게 된다.(불필요한 메모리 사용)
- free 함수를 호출하지 않으면 프로그램 종료 후에도 메모리를 차지하는가?
- 프로그램이 종료되면 프로그램 실행 시 할당된 모든 자원이 반환된다.
C언어 : 할당된 동적 메모리 접근 예제(malloc, free 사용)
아래 두 가지의 malloc와 free의 사용 예시를 확인하자.
1. 동적 메모리 할당의 응용 - 할당된 동적 메모리 접근
#include <stdlib.h>
#include <iostream>
using namespace std;
int main() {
int * ptr1 = (int *)malloc(sizeof(int));
int * ptr2 = (int *)malloc(sizeof(int) * 7);
int i;
// 메모리 할당 실패에 따른 오류의 처리
if (ptr1 == NULL || ptr2 == NULL) {
cout << "메모리 할당을 실패하였습니다." << endl;
return -1;
}
*ptr1 = 20;
for (i = 0; i < 7; i++) {
ptr2[i] = i + 1;
}
cout << "ptr1 값 : " << *ptr1 << endl;
for (i = 0; i < 7; i++) {
cout << "ptr2[" << i << "] 값 : " << ptr2[i] << endl;
}
free(ptr1);
free(ptr2);
return 0;
}
2. 동적 메모리 할당의 응용 - 입력 받은 정수들의 합과 평균을 구하는 예
#include <stdlib.h>
#include <iostream>
using namespace std;
int main() {
// 몇 개의 정수를 입력할 지 물어본다.
int size;
cout << " 몇 개의 정수를 입력하시겠습니까? : ";
cin >> size;
// 필요한 만큼의 메모리를 할당한다.(배열 할당).
int* arr = (int *)malloc(sizeof(int) * size);
// 정수를 입력받는다.
cout << "정수를 입력하시오. : ";
for (int i = 0; i < size; ++i){
cin >> arr[i];
}
// 평균을 계산하고 출력한다.
int sum = 0;
for (int i = 0; i < size; ++i){
sum += arr[i];
}
float avg = (float)sum / (float)size;
cout << "합 = " << sum << " / 평균 = " << avg << endl;
free(arr);
return 0;
}
5. C언어 : calloc 함수
calloc 함수는 메모리를 동적으로 할당하는 C 언어의 표준 함수 중 하나이다. 이 함수는 malloc 함수와 free 함수와 같이 stdlib.h 헤더 파일에 선언되어 있다. calloc 함수는 메모리 블록을 할당하고 그 메모리를 모두 0으로 초기화한다.(자동으로)
calloc 함수의 원형은 다음과 같다:
#include <stdlib.h>
int main() {
// 사용법 : void * calloc(size_t num_elements, size_t element_size);
int * ptr;
ptr = (int *)calloc(5, sizeof(int));
free(ptr);
return 0;
}
calloc 함수는 수행 성공 시 할당된 메모리의 주소 값을 반환하고, 실패 시에는 NULL 값을 반환한다.
- num_elements: 할당할 요소의 개수
- element_size: 각 요소의 크기
calloc 함수는 malloc 함수와 매우 유사하다. calloc은 할당한 메모리 블록의 크기를 num_elements * element_size로 계산하여 할당하고, malloc 함수 역시 메모리를 할당하는 데 필요한 크기만큼만 할당하게 된다. 다만 malloc 함수는 할당한 메모리를 초기화하지 않고, calloc 함수는 할당한 메모리를 모두 0으로 초기화(모든 비트를 0으로 초기화)한다는 차이점이 있다.
따라서, calloc은 메모리를 할당하고 0으로 초기화해야 할 경우에 사용하고 malloc 함수는 단순히 메모리를 할당하고 초기화하지 않으므로 초기화가 필요한 경우에는 개발자가 직접 초기화를 해주어야 한다.
6. C++ : 동적 메모리 할당 - new, delete
- C++에서는 new와 delete라고 하는 별도의 연산자를 제공한다.
- 동적 메모리는 new 연산자를 이용하여 할당한다.
- 사용방법은 new 뒤에 자료형을 적는 식으로 하면 된다.
- 만약 하나 이상의 요소가 필요하다면 [ ] 안에 그 요소의 개수를 적어주면 된다.
// 방법1) int *p1; p1 = new int[5]; => int형 변수 5개를 저장할 수 있는 공간을 동적으로 할당하고 첫 번째 변수를 가리키는 주소를 반환한다. // 방법2) int *p2 = new int[5]{0, 1, 2, 3, 4}; => 특정한 값으로 초기화한 메모리를 사용할 때
- new 연산자는 할당되는 동적 메모리의 시작 주소를 반환한다.
- 동적 메모리 해제할 때는 delete 연산자를 사용한다.
-
// 방법1) delete p; // 방법2) delete [] p; // => 둘다 동적 할당 받은 메모리 공간을 해제한다.
-
C++ : 동적 메모리 할당 & 해제 예제
#include <iostream>
#include <time.h>
using namespace std;
int main() {
int *ptr;
srand(time(NULL)); // 난수 발생기 시드 설정
ptr = new int[10]; // 1. 동적 메모리 할당
for (int i = 0; i < 10; i++){
ptr[i] = rand(); // 2. 동적 메모리 사용
}
for (int i = 0; i < 10; i++){
cout << ptr[i] << " ";
}
delete[] ptr; // 3.동적 메모리 반납
cout << endl;
return 0;
}
7. C++ : 스마트 포인터
- C++ 최신 버전에서 나온 기능이다.
- 스마트 포인터를 사용하면 프로그래머가 동적 메모리 할당 후에 잊어버려도 자동으로 동적으로 메모리가 삭제된다.
- 스마트 포인터는 자동으로 nullptr로 초기화된다.
- 사용 방법은 먼저 memory 라이브러리 include 하고 'unique_ptr<타입명> 변수명(new 타입명);' 이다.
-
#include <iostream> #include <memory> using namespace std; int main() { unique_ptr<int> p(new int); // 스마트 포인터 사용 *p = 99; // p를 사용한다. }
C++ : 스마트 포인터 예제 - 스마트 포인터로 배열 가리키기
#include <iostream>
#include <memory>
using namespace std;
int main() {
unique_ptr<int[]> buf(new int[10]); // 스마트 포인터로 동적 메모리 할당(배열 생성)
for(int i =0; i < 10; i++){
buf[i] = i;
}
for(int i = 0; i < 10; i++){
cout << buf[i] << " ";
}
cout << endl;
return 0;
}
8. 객체의 동적 생성
객체도 동적으로 생성하고 해제할 수 있다. 아래 예제를 참고하자.
#include <iostream>
using namespace std;
class Dog {
private:
string name;
int age;
public:
Dog() {
cout << "생성자 호출\n";
age = 1;
name = "바둑이";
}
~Dog() {
cout << "소멸자 호출\n";
}
};
int main() {
Dog *pDog = new Dog;
delete pDog;
return 0;
}
'Develop > C, C++' 카테고리의 다른 글
[C++] 객체 배열과 벡터 (0) | 2023.12.22 |
---|---|
[C/C++] 상속(Inheritance) (0) | 2023.12.19 |
[C/C++] 문자열(String) (0) | 2023.12.18 |
[C, C++] 포인터(Pointer) (0) | 2023.12.18 |
[C/C++] 함수(Function) (0) | 2023.12.18 |