관리 메뉴

공부 기록장 💻

[C언어] 동적 메모리 할당 - malloc(), calloc(), realloc(), free() 본문

# Language & Tools/C

[C언어] 동적 메모리 할당 - malloc(), calloc(), realloc(), free()

dream_for 2021. 2. 3. 20:37

 

동적 메모리가 필요한 이유?

 

- 배열을 선언할 때, 정확한 요소 개수를 알지 못해 충분한 양의 메모리 공간을 미리 확보해 놓는 경우가 있다. 이처럼 정적 메모리를 사용하는 경우에는, 미리 정해전 크기를 사용해야 하기 때문에 메모리 공간 사용에 있어 매우 비효율적이다. 따라서 프로그램 실행 도중에 필요한 만큼의 메모리를 동적으로 할당하여 사용하도록 하자. 변수가 생성되고 소멸되는 시간을 조절할 수 있으므로 매우 효율적이다. 

 

 

동적 메모리를 사용하기 위해선 기본적으로 할당/사용/반납 의 과정이 필요하다.

 

 

동적 메모리 할당/반납

 

동적으로 메모리 공간을 확보하기 위해선, 운영체제에게 메모리 공간을 요청하는 단계가 필요하다.

<stdlib.h> 헤더 파일의 malloc(), calloc(), realloc() 함수를 이용해 메모리 공간을 동적으로 할당할 수 있다.

사용이 끝나면 해당 메모리는 free() 함수를 이용하여 반납한다.

 

 

void *malloc(size_t size);

- 기능: 메모리를 동적으로 할당하고 포인터를 반환
- 인수: 할당할 메모리의 바이트 크기
- 반환값: 할당한 메모리의 포인터, 실패 시 NULL

 

기본적으로 쓰이는 malloc() 함수.

사용할 바이트 수를 인수로 전달하면, 해당 메모리 공간의 시작 주소를 반환하는 함수이다.

함수의 반환형은 void 인데, 사용자가 정수, 실수, 문자 등 여러가지 종류의 자료형의 메모리를 동적으로 할당할 수 있기 때문.

따라서 malloc() 함수를 사용하여 반환값을 포인터에 대입할 때, 명시적으로 형변환을 해주어야 한다.

 

int* p = NULL; // int형 포인터 선언
p = (int*)malloc(sizeof(int) * 100); // int형 4바이트를 100개만큼 할당 (total 400 bytes)하여 int형으로 형변환 한 후, 포인터에 대입

// int *p = (int*)malloc(sizeof(int) * 100); 

if (p == NULL) // 동적 할당에 실패한 경우
{
	printf("동적 메모리 할당 오류\n");
	exit(1);
}

 

포인터가 미리 선언되어 있는 경우,

해당 포인터에 malloc() 함수를 호출하여 얻은 메모리 공간을 대입하여 주면 된다.

자료형은 포인터의 자료형과 일치해야 한다.

 

메모리 공간이 부족한 경우, 메모리를 할당하지 못하기 때문에 확인해 주는 코드를 작성해준다.

 

 

 

void *calloc(size_t nitems, size_t size);

- 기능: 메모리를 동적으로 할당하고 0으로 치과한 후에 포인터를 반환
- 인수: 할당할 항목 수, 항목 하나의 크기
- 반환값: 할당한 메모리의 포인터, 실패 시 NULL

calloc() 함수는 0으로 초기화된 동적 메모리를 할당한다. 바이트 단위가 아닌, 항목 단위로 메모리를 할당한다.

 

void *realloc(void *ptr, size_t size);

- 기능: 동적 할당한 메모리의 크기를 재조정
- 인수: 이전에 할당받은 영역의 포인터, 재조정한 후의 전체 바이트 크기
- 반환값: 새로 할당한 메모리의 포인터, 실패 시 NULL

 

realloc() 함수는 할당하였던 메모리 블록의 크기를 변경한다.

기존의 동적 메모리 블록을 가리키던 포인터와 새로운 메모리 블록의 바이트 수를 인수로 받아

새로 변경된 메모리의 주소를 반환한다.

 

 

 

calloc() 함수와 realloc() 함수를 이용해 동적 메모리를 할당하면 다음과 같다.

 

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

int main(void) {

	int* p = NULL; // int형 포인터 선언
	p = (int*)calloc(5, sizeof(int)); // calloc() 함수를 이용하여 5개의 항목을 0으로 초기화하여 int형 정수의 메모리 크기인 4바이트만큼 할당한다. 총 20바이트 할당

	for (int i = 0;i < 5;i++)
		printf("%d ", *(p + i)); // 각 포인터가 가리키는 값을 출력
	printf("\n");

	// 동적 할당한 메모리 블록의 크기를 구하기 위해선 _msize() 함수를 사용한다.
	printf("sizeof(p)/sizeof(int) : %d \n", _msize(p) / sizeof(int));

	p = realloc(p, 7*sizeof(int)); // realloc() 함수를 이용하여 기존의 메모리 영역의 크기를 변경한다.
	if (p == NULL) {
		printf("동적 메모리 할당 오류\n");
		exit(1);
	}

	printf("sizeof(p)/sizeof(int) : %d \n", _msize(p) / sizeof(int));
    
  	free(p); // 동적 메모리 반납

	return 0;
}

 

포인터가 가리키는 값을 모두 출력해보면, calloc() 함수가 0으로 초기화된 동적 메모리를 할당한 것을 확인할 수 있다.

sizeof() 연산자를 이용해 정적으로 할당된 stack 영역의 메모리의 크기를 구할 수 있는 반면,

heap 영역의 동적 메모리는 _msize() 를 이용해 그 크기를 구할 수 있다.

realloc() 이전에는 메모리 항목이 5개, 변경 이후엔 7개임을 확인할 수 있다.

 

 

 

동적 메모리 사용

 

일반적인 정수, 실수, 문자 등의 자료형 뿐 아니라 구조체를 저장할 수 있는 공간도 할당 받을 수 있다.

구조체 배열에 대한 메모리를 확보하려면, 구조체의 크기에 배열 요소의 개수를 곱해주면 된다.

 

 

학생의 학점, 이름을 멤버로 갖는 구조체 배열을 동적으로 할당받아

정보를 입력받고 이를 출력하는 예시를 살펴보면 다음과 같다.

 

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

// 정수, 문자열을 멤버로 갖는 구조체 선언
typedef struct {
	int number;
	char name[10];
}STUD;

int main(void) {

	STUD* s = NULL;
	s = (STUD*)malloc(2 * sizeof(STUD)); // 구조체의 자료형과 구조체 배열 크기에 따라 메모리 할당

	if (s == NULL) {
		printf("동적 할당 오류\n");
		exit(1);
	}

	// 구조체 배열의 요소에 대한 정보를 입력받는다.
	for (int i = 0;i < 2;i++) {
		printf("학번: ");
		scanf_s("%d", &s[i].number);
		printf("이름: ");
		scanf_s("%s", s[i].name, 10);
	}

	// 구조체 배열의 요소에 대한 정보 출력
	for (int i = 0;i < 2;i++)
		printf("%d : %s\n", s[i].number, s[i].name);
	
	free(s);

	return 0;
}

 

각 배열 원소에 인덱스를 이용해 접근할 수 있다.

 

728x90
반응형
Comments