juuuding
CH 25 메모리 관리와 메모리의 동적 할당 본문
1. C언어의 메모리 구조
1. 메모리의 구성
프로그램 실행 시 운영체제에 의해 마련되는 메모리 구조는 다음과 같이 네 개의 영역으로 구분이 된다. 그 이유는 유사한 성향의 데이터들을 묶어서 저장하면, 관리가 용이해지고 메모리의 접근 속도가 향상되기 때문이다.
2. 메모리 영역별로 저장되는 데이터 유형
[코드 영역]
실행할 프로그램 코드가 저장되는 메모리 공간이다. CPU는 코드 영역에 저장된 명령문들을 하나씩 가져가서 실행한다.
[데이터 영역]
전역변수와 static 변수가 할당된다. 프로그램의 시작과 동시에 메모리 공간이 할당되어 프로그램 종료 시까지 남아있게 된다.
[스택 영역]
지역변수와 매개변수가 할당된다. 함수를 빠져나가면 소멸된다는 특징이 있다.
[힙 영역]
데이터와 스택영역에 있는 데이터들은 생성과 소멸의 시점이 정해져 있는데, 이와 다르게 프로그래머가 원하는 시점에 변수를 할당하고 또 소멸을 지원힌다.
3. 프로그램 실행에 따른 메모리의 상태 변화
프로그램이 종료되면 운영체제에 의해 할당된 매모리 공간 전체를 반환하는데, 그때가 전역변수가 소멸되는 시점이다. 데이터는 스택영역에 스택 형식으로 데이터가 들어갔다가 소멸된다.
2. 메모리의 동적 할당
1. 전역변수와 지역변수로 해결되지 않는 상황
함수가 매번 호출될 때마다 새롭게 할당되고 또 함수를 빠져나가도 유지가 되는 유형의 변수가 필요하다. 즉, 지역변수처럼 함수가 호출될 때마다 매번 할당이 이뤄지지만, 할당이 되면 전역변수와 마찬가지로 함수를 빠져나가도 소멸되지 않는 성격의 변수가 필요하다는 것이다. 이는 malloc과 free로 힙 영역에 할당하고 소멸할 수 있다.
2. 힙 영역의 메모리 공간 할당과 해제: malloc과 free 함수
int main(void) {
void* ptr1 = malloc(4); //4바이트가 힙 영역에 할당
void* ptr2 = malloc(12); // 12바이트가 힙 역역에 할당
free(ptr1); //ptr1이 가리키는 4바이트 메모리 공간 해제
free(ptr2); //ptr2가 가리키는 12바이트 메모리 공간 해제
}
ptr1은 첫 번째 malloc 함수호출을 통해 할당된 메모리 공간의 첫 번째 바이트를 가리키게 되고, ptr2는 두 번째 malloc 함수호출을 통해 할당된 메모리 공간의 첫 번째 바이트를 가리킨다. ptr1을 인자로 free 함수를 호출하는 시점에 ptr1이 가리키는 메모리 공간이 소멸되고, ptr2을 인자로 free 함수를 호출하는 시점에 ptr2이 가리키는 메모리 공간이 소멸된다. 여기서 주의사항은 힙에 할당된 메모리 공간은 포인터를 이용해서 메모리에 접근해야 한다는 것이다.
3. malloc 함수의 반환형이 void형 포인터인 이유와 힙 영역으로의 접근
malloc 함수는 원하는 크기만큼 메모리 공간을 할당하고 그 메모리의 주소 값을 반환한다. 여기에 추가로 어떻게 사용할지는 포인터 형의 변환을 통해 직접 결정할 수 있다.
int main(void) {
int* ptr1 = (int *)malloc(sizeof(int));
double* ptr2 = (double *)malloc(sizeof(double));
}
#define CRT_SECURE_NO_WARNIGNS
#include <stdio.h>
int main(void) {
int* ptr1 = (int *)malloc(sizeof(int));
int* ptr2 = (int *)malloc(sizeof(int));
int i;
*ptr1 = 20;
for (i = 0; i < 7; i++){
ptr2[i] = i + 1;
}
printf("%d\n", *ptr1);
for (i = 0; i < 7; i++) {
printf("%d", ptr2[i]);
}
free(ptr1);
free(ptr2);
return 0;
//20
//1 2 3 4 5 6 7
}
참고로 malloc 함수는 메모리 공간이 할당에 실패할 경우 NULL을 반환한다. 그리고 malloc 함수의 호출을 통한 메모리 공간의 할당을 가리켜 '동적 할당(dynamic allocation)'이라고 한다.
4. free 함수를 호출하지 않으면 프로그램 종료 후에도 메모리가 남는가?
그렇지 않다. 프로그램이 종료되면 운영체제에 의해서 모든 메모리 공간이 해제된다. 그래서 free 함수가 필요없는 코드라도 습관적으로 free 함수를 호출하는 것이 좋다.
5. 문자열을 반환하는 함수를 정의하는 문제의 해결
#define CRT_SECURE_NO_WARNIGNS
#include <stdio.h>
char* ReadUserName(void) {
char* name = (char*)malloc(sizeof(char) * 30);
printf("What's your name?");
gets(name);
return name;
}
int main(void) {
char* name1;
char* name2;
name1 = ReadUserName();
name2 = ReadUserName();
printf("name1: %s\n", name1);
printf("name2: %s\n", name2);
free(name1);
free(name2);
return 0;
}
6. malloc 함수의 사촌 뻘 되는 calloc 함수
calloc 함수도 힙 영역에 메모리 공간을 할당하는 함수로 malloc과의 유일한 차이점은 메모리 공간의 할당을 위한 인자의 전달방식에 있다.
void calloc(size_t elt_count, size_t elt_size); // 성공 시 할당된 메모리의 주소 값, 실패 시 NULL 반환
이처럼 calloc은 인자로 2가지를 전달 받는다. malloc 함수가 "총 120 바이트를 힙 영역에 할당해줘"라고 한다면, calloc 함수는 "4바이트 크기의 블록(elt_size) 30개를(elt_count) 힙 영역에 할당해줘"라고 하는 것이다.
7. 힙에 할당된 메모리 공간 확장 시 호출하는 realloc 함수
한번 할당된 메모리 공간은 그 크기를 확장시킬 수 없지만 realloc 함수를 사용하면 이것이 가능해진다.
void * realloc(void * ptr, size_t size); //성공 시 새로 할당된 메모리의 주소 값, 실패 시 NULL 반환
첫 번째 전달 인자로 확장하고자 하는 힙 메모리의 시작 주소 값을 전달하고, 두 번째로 확장하고자 하는 메모리의 전체 크기를 전달한다. "ptr이 가리키는 메모리의 크기를 size 크기로 조절해줘"라는 의미이다.
int * arr = (int *)malloc(sizeof(int)*3); //길이가 3인 int형 배열 할당
int * arr = (int *)realloc(sizeof(int)*5); //길이가 5인 int형 배열로 확장
위 코드의 실행 결과는 두가지로 나뉜다.
1. malloc 함수가 반환하는 주소 값과 realloc 함수가 반환한 주소 값이 같은 경우
2. malloc 함수가 반환하는 주소 값과 realloc 함수가 반환한 주소 값이 같지 않은 경우
1은 기존 할당 메모리 공간의 뒤를 이어 확장할 영역이 넉넉한 경우이고 2는 그렇지 않은 경우이다. 2의 경우에는 힙의 새로운 위치에 메모리 공간을 별도로 할당해서 이전 배열에 저장된 값을 복사한다.
'C언어 > 윤성우의 열혈 C' 카테고리의 다른 글
CH23 구조체와 사용자 정의 자료형2 (0) | 2023.03.28 |
---|---|
CH 22 구조체와 사용자 정의 자료형 1 (0) | 2023.03.23 |
CH 21 문자와 문자열 관련 함수 (0) | 2023.03.21 |
CH 19 함수 포인터와 void 포인터 (0) | 2023.03.20 |
CH 18 다차원 배열과 포인터의 관계 (0) | 2023.03.17 |