[C] 메모리 할당 방법

2023. 12. 24. 17:20자료구조

Contents 접기

내 아픈 과거

9살 때 집에서 생일파티를 한 적이 있다.

친구들이 많이 올 것이라 예상하고, 치킨, 피자 등 여러가지 음식을 준비했으나

갑자기 친구들이 일이 생겨서 2명 밖에 오지 않았고,

그래서 음식을 다 남겼던 아직까지도 슬픈 기억이 있다..

 

 

누군가는 이 이야기를 듣고 다음과 같이 얘기할 수 있다.

 

제목을 메모리 할당으로 지어놓고
갑자기 생일파티?

 

 

오늘 얘기할 메모리 할당과 연관지어 보자.

 

 

동적 할당 vs 정적 할당

정적 (Statics)

처음 내 생일 파티에 참석할 친구 수는 미리 알려져 있었다.

따라서, 생일 파티 전날에 친구 수를 확인하고, 정해진 친구 수에 맞게 음식과 접시를 미리 준비하면 된다.

파티가 시작되면 이미 준비된 음식과 접시를 사용하며, 나중에 친구가 오더라도 새로운 준비를 할 필요가 없는 것!

 

프로그램에서 정적할당은 이처럼 프로그램이 시작되기 전 미리 정해진 크기 메모리 할당하는 것이다.

정확한 숫자로 자원을 할당하면, 메모리 할당 및 해제의 번거로움이 줄어들지만,

미리 예측하여 할당해야 하므로 유연성이 부족할 수 있다

 

장점: 정적 할당된 메모리는 프로그램이 종료될 때 알아서 운영체제가 회수하므로 메모리 누수 문제 발생하지 않음
단점: 스택에 할당된 메모리이므로 동적 할당에 비해 할당 받을 수 있는 최대 메모리에 제약을 받음

 

# c언어 정적 할당 예
# buffer가 심하게 낭비되고 있는 모습

int i, j;
int buffer[100];
char name[] = "data structure";

 

 

동적 (Dynamic)

반면, 생일 파티에 참석할 친구 수를 미리 알고 있지 않았다고 생각해보자.

그렇다면 친구 한 명이 도착할 때마다 새로운 음식과 접시를 그때그때 준비해줘야 하고,

만약 다른 친구가 오기 전 먼저 온 친구가 떠나면 자리를 치워줘야 한다.

 

 

프로그램에서의 동적 할당은 실행 시간(파티) 동안 사용할 메모리 공간(음식과 접시)을 할당하는 것이다.

 

즉, 프로그램 실행 중 필요한 메모리 공간을 동적으로 할당하고,

이 할당된 메모리 공간은 메모리 반납이나 Garbage Collection이 발생하기 전까지 계속 유지된다.

주로 프로세스의 힙(heap) 영역에서 할당되며, 프로세스가 종료되면 해당 메모리 resource가 반납되어 해제된다.

 

힙 영역에서 동적으로 할당된 메모리 공간은 프로세스의 힙 영역을 벗어나면 자동으로 메모리가 해제되지 않는다.

따라서 메모리 관리자가 명시적으로 할당 해제를 해줘야 한다.

또한, 할당된 힙 영역을 넘어서 더이상의 메모리 할당 요구가 있을 경우 할당되지 않는다.

 

#include <stdlib.h> //동적 메모리 관련 함수 불러오기

...
int *ptr = NULL;
int size = 100;

ptr = (int*) malloc (sizeof(int)*size); // malloc() 메모리 할당
...

free ( ptr ); // 메모리 해제

 

장점: 상황에 따라 원하는 크기만큼의 메모리 할당 ➜ 경제적
단점: 더이상 사용하지 않을 때 명시적으로 메모리 해제

 

 

C언어 에서의 동적 할당 방법 

함수 기능
void *malloc(size_t size); size byte의 메모리를 힙에서 할당하여 반환
void *calloc(size_t num, size_t size); (num *size) byte의 메모리를 힙에서 할당 후 포인터값을 반환
void *realloc(void *ptr, size_t size); ptr이 가리키는 메모리를 size byte만큼 힙에서 재할당 후 반환
void free(void *ptr); ptr이 가리키는 메모리 해제
해제 전까지 존재하므로 사용하지 않으면 반납

 

동적 할당 함수를 사용한 코드이다.

#include <stdio.h>
#include <stdlib.h>

int main(){
	// malloc을 사용하여 5개의 정수 크기 만큼의 메모리 동적 할당
	int* dynamicArray = (int*)malloc(5 * sizeof(int));

	if (dynamicArray == NULL) {
		printf("메모리 할당 오류\n");
	}

	// 메모리에 값 저장
	for (int i = 0; i < 5; ++i)
		dynamicArray[i] = i + 1;

	// 할당된 메모리의 값 출력
	printf("할당된 메모리의 값 (malloc): ");
	for (int i = 0; i < 5; ++i)
		printf("%d ", dynamicArray[i]);

	// calloc을 사용하여 3개의 정수 크기만큼의 메모리 동적 할당
	int* callocArray = (int*)calloc(3, sizeof(int));

	if (callocArray == NULL) {
		printf("메모리 할당 오류\n");
	}

	// 할당된 메모리의 값 출력(calloc은 0으로 초기화)
	printf("\n할당된 메모리의 값 출력 (calloc): ");
	for (int i = 0; i < 3; ++i)
		printf("%d ", callocArray[i]);

	// realloc을 사용하여 동적으로 할당된 메모리 크기 조정
	int* reallocatedArray = (int*)realloc(dynamicArray, 8 * sizeof(int));

	if (reallocatedArray == NULL)
		printf("메모리 재할당 오류\n");

	// 새로 할당된 메모리의 값 출력 (기존의 값 유지, 추가된 부분은 초기화되지 않음)
	printf("\n재할당된 메모리의 값 (realloc): ");
	for (int i = 0; i < 8; ++i)
		printf("%d ", reallocatedArray[i]);

	// 메모리 해제
	free(callocArray);
	free(reallocatedArray);

	return 0;
}

 

C언어에서는 malloc, calloc 함수로 동적 할당한다.

만약 영역에 여유가 있다면 할당하고 그 메모리주소 값을 넘기고, 실패하면 NULL값을 넘긴다.

사용이 완료되면 해당 주소 값을 넘겨 해제하면 재할당이 가능한 상태가 된다.

 

malloc() vs calloc()

특징 비교 malloc calloc
인자의 차이 할당하고자 하는 메모리의 byte 크기 1. 할당하고자 하는 메모리 블록의 개수
2. 각 블록의 크기
초기화 여부 초기화 x (더미값 존재 가능) 0으로 초기화
용도 크기가 큰 단일 메모리 블록 할당 시 배열과 같은 구조체 다룰 때 유용
성능 'calloc'이 'malloc' 후에 초기화를 진행하는 것보다
'calloc' 자체에서 초기화를 수행하므로 'calloc'은 초기화가 필요한 경우 더 효율적