C언어 배열 free - ceon-eo baeyeol free

안녕하세요! 오늘은 6번째 시간입니다. 7번째 시간에는 구조체를 다루도록하고 8번째 시간에는 포인터를 다루도록 하겠습니다. 근데 이번 시간에 포인터와 연관되는 것이 나오는데 아주 간략하게만 설명하도록 하겠습니다.

먼저 소스코드를 보시겠습니다.

C언어 배열 free - ceon-eo baeyeol free

Line 3 : b라는 변수가 int형 포인터로 선언이 되었습니다. 이 말이 무슨 뜻인가 하면 배열을 배웠던거 기억 나시나요? 배열로 선언된 변수는 0번째 원소의 주소 값으로 그 배열의 주소를 찾아냅니다. 왜냐하면 0번째 원소부터 순서대로 마지막 원소까지 있기 때문인데요. 포인터는 조금 다릅니다. 이 포인터라는 변수는 int 자료형을 가진 변수를 가리키는 역할을 합니다. 말 그대로 주소값을 저장해놓고 만약 컴파일 시에 그 주소값에 해당하는 값이 필요하다면 그 주소값에 해당하는 값을 사용할 수 있도록 가리키는 역할을 합니다.

Line 7 : 이제 이번 시간에 배울 내용인 malloc함수에 대해서 나왔는데요. 나름 간단합니다. b가 무슨 자료형을 가진 변수인지 (int*)라고 표시를 해주고 이제 오른쪽 함수 사용할 때에도 (int *)를 사용하여 b와 같은 자료형들로 동적 할당을 받을 수 있게 해주고 malloc 내부의 매개변수는 int 자료형의 size(4byte) * 할당받을 개수 로 사용합니다. 만약 5를 입력하면 20byte만큼의 공간이 생길 것이고 6을 입력한다면 24byte만큼의 공간이 b에게 주어지겠네요.

Line 8 : b가 할당 받은 메모리의 크기를 출력해주는 것입니다. 출력을 하게 될 경우 int 자료형의 크기 : 4byte * 입력받은 개수 = 총 할당받은 크기를 출력하도록 하는 것입니다.

Line 9~13 : 먼저 for문이 돌아가는데 i가 0번째 원소부터 N-1번째 원소까지 반복을 합니다. 그 동안에 내부 동작은 *는 가리킨다라고 읽어주시면 됩니다. i가 0번째 원소일 때 for문의 동작을 보시면 'b+0이 가리키는 값을 0으로 집어넣어라.' 라는 의미가 됩니다. 따라서 i가 0일 때  *(b+i)를 출력한다면 b+0이 가리키는 값을 0으로 집어넣었으니 0이 출력이 될 것입니다. 마찬가지로 for문이 동작하여 i가 1일 경우에는 *(b+1)에 1을 집어넣었으니 1이 출력이 될거고 for문 동작이 N-1번째 까지 가게 된다면 *(b+N-1) = N-1; 이니까 출력을 하게 될 경우 N-1이 될 것입니다.

Line 14 : free( 동적할당 받은 변수 ) 를 사용하시게 된다면 메모리에 있던 그 할당받은 내용들을 지워줍니다. 이게 메모리 관리 문제에 직면할 수 있으니 malloc()함수를 사용한다면 free를 해줘야겠군.이라고 생각하시면 됩니다.

출력창을 보실까요?

C언어 배열 free - ceon-eo baeyeol free

네 일단 N으로 4를 입력 받았고 b가 할당받은 메모리는 int 자료형의 크기(4byte) * N(4)를 해줘서 16byte다. 라고 출력이 되었고 아래에 차례대로 Line 9~13 에 했던대로 0,1,2,3 즉, N-1까지의 출력이 되었다는 것을 알 수 있습니다.

이 함수는 동적으로 메모리를 관리할 때에 사용하기 편하게 해줍니다. 정적인 메모리 할당보다 동적인 메모리 할당이 훨씬 편합니다. 왜냐하면 100개를 할당 받는데 그 이상으로 할당을 해줘야할 경우에는 오버플로우나 100개는 필요없고 한 50개만 할당을 받으면 되는데 100개를 할당해서 메모리 낭비를 시킬 수가 있습니다. 따라서 유연하게 malloc()함수를 사용해주신다면 좀 더 효율적인 코드를 짜실 수 있으실 겁니다. 나중에 좀 심화된 예제를 올릴테니 그 때 확실히 이해하셨으면 좋겠습니다!

이 레퍼런스의 모든 내용은 여기를 기초로 하여 작성하였습니다.

아직 C++ 에 친숙하지 않다면 씹어먹는 C++ 은 어때요?

stdlib.h 에 정의됨

void free(void* ptr);

메모리를 해제한다

이전에 malloc 혹은 calloc, realloc 등으로 할당된 메모리를 해제해서, 나중에 다시 사용될 수 있게 합니다.

만일 ptr 이 위의 함수들도 할당된 메모리를 가리키지 않는다면, 무슨일이 일어날지는 아무도 모릅니다(undefined behavior).

만일 ptr 이 널 포인터라면, 위 함수는 아무것도 하지 않습니다.

기존에 malloc, callor, realloc 으로 할당된 메모리의 시작점을 가리키는 포인터

없음

/* free example */
#include <stdlib.h> /* malloc, calloc, realloc, free */

int main() {
  int *buffer1, *buffer2, *buffer3;
  buffer1 = (int*)malloc(100 * sizeof(int));
  buffer2 = (int*)calloc(100, sizeof(int));
  buffer3 = (int*)realloc(buffer2, 500 * sizeof(int));
  free(buffer1);
  free(buffer3);
  return 0;
}

실행 결과

실행 결과

(잘 실행 된다.)
  • malloc : 메모리를 할당한다.

  • calloc : 0 으로 초기화된 메모리를 할당한다.

  • realloc : 메모리를 재할당 한다.

포인터와 배열의 관계

📌 포인터와 배열은 밀접한 관계를 갖고 있으나, 분명한 차이점이 있다.

📌 배열은 선언하게 되면 기본적으로 배열 자기 자신의 시작 주소를 갖고 있다.

#include <stdio.h>

int main() {
	char str[100];

	printf("str의 주소 : %p\n", &str);
	printf("str의 값 : %p\n", str);

	printf("입력 : ");
	scanf("&s", str);

	printf("출력 : %s\n", str);
	return 0;
}
#include <stdio.h>

int main() {

	char str1[10] = "Hello";
	char* str2 = "Abcde";

	/*printf("atr1 : %s\n", str1);
	printf("str1 : %s\n", str2);*/

	str1[0] = 'B';
	str2[0] = 'Z';

	printf("str1 = %c%c%c%c%c\n",str1[0], str1[1], str1[2], str1[3], str1[4]);
	printf("str2 = %c%c%c%c%c\n", str2[0], str2[1], str2[2], str2[3], str2[4]);

	return 0;
}

동적 할당

  • 메모리 공간을 원하는 시점에 원하는 크기 만큼 할당 받고 그 공간을 사용 후, 원하는 시점에 해제하는 방식
  • 메모리 공간을 할당 받을 때는 malloc()함수를 사용해서 할당 받을 수 있다.
  • 이 때, 반드시 stdlib.h 헤더파일을 선언해주어야 한다.
  • malloc() 함수의 원형 : 주소를 저장할 변수 = malloc(크기);
  • 동적 할당을 통해서 할당 받은 메모리 공간은 Heap 영역에 해당한다.
  • 동적 할당 받은 메모리 공간을 해제할 때는free(해제할 공간의 주소); 형식으로 해제할 수 있다.
  • 한 번의 동적 할당에는 반드시 한 번의 동적 해제가 진행되어야 한다.
  • malloc()함수에서 크기를 넘겨 줄 때에는 기본적으로 sizeof()연산자를 이용해서 넘겨준다.
#include <stdio.h>
#include <stdlib.h>

int main() {

	int* ptr;
	ptr = (int*)malloc(4); //void 포인터 형태의 값을 반환
	*ptr = 10;
	printf("출력 : %d\n", *ptr);
	free(ptr);
	return 0;
}
#include <stdio.h>
#include <stdlib.h>

int main() {

	int* ptr;
	ptr = (int*)malloc(sizeof(int)*34);

	ptr[0] = 10;
	ptr[1] = 20;
	
	printf("츨력 : %d %d\n", ptr[0], ptr[1]);
	free(ptr);

	return 0;
}