SW/C

[동적메모리할당] malloc, calloc 사용법

Gangdor 2021. 9. 2. 18:32
반응형

malloc, calloc은 메모리 동적할당에 쓰인다. 아래를 보자.

int N=4;
int arr[N];
//int arr[4];

위의 코드는 실행 가능할까? C언어 컴파일러의 버전에 따라 실행 가능 할 수도 있고 가능하지 않을 수도 있다.

VLA를 지원하는지 하지 않는지에 따라 다르다. C99버전에서는 VLA를 지원하나 구버전에서는 기대할 수 없다. 구버전을 예로 든다면 아쉽게도 에러가 난다. 배열은 변수로서 선언할 수 없기 때문이다. 왜 안되는 걸까?

메모리 구조를 먼저 알아야한다. N이 지역변수임을 가정하면 N에 대한 메모리크기는 컴파일타임에 정해진다. 4byte일 것이다. 그러나 N의 값이 4라고 인식되는 것은 컴파일타임이 아닌 런타임이다. 따라서 프로그램이 수행되어야 한다는 뜻이다. 마찬가지로 arr배열도 컴파일타임에 메모리크기가 할당되겠으나 런타임 전이므로 배열의 크기인 N이 얼마인지 모른다. 4인지 모른다. 따라서 에러가 발생하는 것이다.

 

동적메모리할당

그렇다면 프로그램 동작중에 메모리를 할당할 수 는 없을까? 바로 이때 사용되는 것이 malloc과 calloc이다.

일반적으로 엠얼록, 씨얼록이라고 읽지만 멜록, 콜록이라고도 부르는 경우도 있다. 편한대로 부르자. 이 두 함수는 stdlib.h에 정의되어 있다. 따라서 이 두 함수를 사용하려면 include를 해주도록 하자.

#include <stdlib.h>

동적메모리는 heap영역에 할당된다. 일반적인 매개변수나 지역변수들이 stack 영역에 할당되는 것과 다른 점이다. 프로그램 수행중에 사용자에 의해 할당을 되기때문에 할당 후 해제 역시 해주어야 한다. 즉 수동적인 메모리할당이다. free()함수로 해제를 해주게 되는데 프로그램 수행도중에 free를 제대로 해주지 않는다면 프로그램 동작에 이상을 발생시킬 수 있다. 그럼에도 큰 크기의 메모리를 동적으로 할당할 때는 유용한 기능이다. 사용법을 알아보자.

 

malloc() 사용법

malloc은 파라미터로 size를 가진다. 즉, 동적으로 할당하고자 하는 메모리의 사이즈를 파라미터로 전달하는 것이다. 예제 코드를 보자.

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

int main()
{
	int N=4;
	int arr[4]; //stack메모리 영역 할당

	int* p = (int*)malloc(N*sizeof(int)); //동적메모리할당, heap 영역
	
	printf("%#x\n",arr);
	printf("%#x",p);
	
	free(p); //동적메모리 해제
	return 0;
}

arr배열은 우리가 흔히 쓰는 배열 선언이다. 앞서 설명과 같이 컴파일타임에 stack에 할당될 것이다. 반면 malloc은 heap영역에 런타임중에 할당된다. malloc은 할당 성공시에 할당한 포인터주소를 return하는데 void*로 정의되어 있기때문에 강제형변환을 해주어야한다. 파라미터로는 int사이즈의 4배만큼의 크기를 전달했다. printf로 주소를 찍어보면 서로 다른 주소임을 확인할 수 있다. 또한 프로그램 종료 전에 메모리누수를 막기 위해 반드시 free(동적할당메모리주소) 로 해제를 해주어야 한다.

malloc 요약
1. stdlib.h include
2. (void*)malloc(size)로서 매개변수는 사이즈를, 리턴값은 할당한 주소값
3. 사용패턴 : 할당 - 사용 - 해제

 

calloc() 사용법

calloc은 파라미터로 count, size를 가진다. malloc을 이해했으니 쉽게 이해할 수 있을 것이다. 아래 예제를 보자.

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

int main()
{
	int N=4;
	int arr[N]; //VLA

	int* mp = (int*)malloc(N*sizeof(int)); //동적메모리할당
	int* cp = (int*)calloc(4,sizeof(int)); //동적메모리할당
	
	printf("%#x\n",arr);
	printf("%#x\n",mp);
	printf("%#x\n",cp);
	
	//출력
	for(int i=0; i<4; i++)
	{
		printf("%d ",arr[i]);
	}
	printf("\n");
	for(int i=0; i<4; i++)
	{
		printf("%d ",*(mp+i));
	}
	printf("\n");
	for(int i=0; i<4; i++)
	{
		printf("%d ",*(cp+i));
	}
	printf("\n");
	
	free(mp); //동적메모리 해제
	free(cp); //동적메모리 해제
	return 0;
}

위의 예제 코드에서는 VLA를 통해 배열을 선언하고, malloc, calloc을 통해 동적 메모리 할당을 하였다.

calloc을 보면 파라미터값 2개를 넘겨주고 있는데, 이부분이 malloc과의 사용 차이점이다. malloc은 전체 사이즈를 넘겨주는 반면, calloc은 방의 개수와 방의 한 사이즈를 넘겨준다. 크게 어려운 부분은 아니다.

할당 후 초기화를 거치지 않고 바로 출력을 해보자.

위와 같은 결과를 얻을 수 있는데, VLA통한 배열 주소값과 동적할당을 통해 주소값의 범위가 0x9xx와 0x1xx로 다른 것을 알 수 있다. 환경에 따라 다르겠지만 필자의 빌드 환경에서는 0x9xx는 stack 영역이며 0x1axx는 heap영역임을 알 수 있다. 또한 VLA 배열은 초기화를 하지 않아, 쓰레기값이 출력되는 것을 알 수 있다.

반면 동적할당 malloc과 calloc은 0으로 초기화가 되어 있는 것을 알 수 있는데 사실 malloc은 0으로 초기화 해주지 않으며 calloc은 할당과 동시에 0으로 초기화해주는데 그 차이점이 있다. malloc이 0으로 초기화 해주는 것에 대해서는 OS나 환경 등에서 차이가 있는데 우선 calloc이 0으로 초기화 해주는 것이 기본 개념이라고 이해하도록 하자.

 

※ stackoverflow에 관련 논의내용이 있으니 더 깊게 보고 싶으면 참고하도록 하자.

https://stackoverflow.com/questions/45323930/is-malloc-initializing-allocated-array-to-zero

 

Is malloc() initializing allocated array to zero?

Here is the code I'm using: #include #include int main() { int *arr; int sz = 100000; arr = (int *)malloc(sz * sizeof(int)); int i; for (i = ...

stackoverflow.com

 

free()

동적 메모리 할당 사용 후에는 반드시 free를 통해 할당된 메모리를 해제해주어야 한다. free 호출 시 해제하고자 하는 메모리 포인터변수를 넘겨주면 되겠다. 해제를 하지 않을 시에는 메모리누수 현상이 발생하여 프로그램이 오작동을 일으킬 수 있으니 꼭 기억하도록 하자

 

 

반응형