메모리 관리 in C

안녕하세요, 미래의 코딩 마법사 여러분! 오늘 우리는 C에서의 메모리 관리라는 흥미로운 세상으로 뛰어들어 보겠습니다. 프로그래밍에 새로운 사람이라면 걱정하지 마세요; 저는 여러분을 이 여정을 단계별로 안내해 드릴 것입니다. 수년 동안 수많은 학생들을 가르쳐온 경험을 바탕으로 말이죠. 그럼, 가상의 헬멧을 쓰고, 컴퓨터 메모리의 건설 현장을 탐험해 보겠습니다!

C - Memory Management

C에서의 동적 메모리 관리 함수

우리가 메모리의 고층 빌딩을 짓기 전에, 사용할 도구들에 대해 익숙해져 보겠습니다. C에서는 메모리를 동적으로 관리해주는 함수 집합을 제공합니다. 이 함수들은 당신의 신뢰할 수 있는 도구 상자라고 생각해 보세요:

함수 목적
malloc() 메모리 블록을 할당합니다
calloc() 여러 개의 메모리 블록을 할당하고 초기화합니다
realloc() 이전에 할당된 메모리 블록의 크기를 조정합니다
free() 할당된 메모리를 시스템에 반환합니다

이 함수들은 우리 메모리 건물의 건설 현장에서 일하는 작업반입니다. 각각의 함수는 자신만의 특별한 역할을 가지고 있으며, 우리는 그 모두를 깊이 이해하게 될 것입니다.

동적 메모리 할당

상상해 보세요, 파티를 계획하고 있지만 얼마나 많은 손님들이 올지 모르는 경우. 이때 동적 메모리 할당이 유용하게 쓰입니다! 고정된 수의 의자를 준비하는 대신, 필요에 따라 추가하거나 제거할 수 있습니다. 그럼 C에서 이를 어떻게 하는지 살펴보겠습니다.

malloc() 함수

우리의 첫 번째 메모리 할당 슈퍼 헴어는 malloc()입니다. 이 함수는 시스템으로부터 메모리 블록을 요청하는 데 사용됩니다.

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

int main() {
int *numbers;
int size = 5;

numbers = (int*)malloc(size * sizeof(int));

if (numbers == NULL) {
printf("메모리 할당 실패!\n");
return 1;
}

for (int i = 0; i < size; i++) {
numbers[i] = i * 10;
printf("numbers[%d] = %d\n", i, numbers[i]);
}

free(numbers);
return 0;
}

이를 간단히 설명하자면:

  1. <stdlib.h>을 포함시키는 이유는 malloc()이 이 파일에 정의되어 있기 때문입니다.
  2. numbers 포인터를 선언하여 동적으로 할당된 배열을 저장합니다.
  3. malloc(size * sizeof(int))은 5개의 정수를 저장할 수 있는 메모리를 요청합니다.
  4. malloc()의 결과를 (int*)로 캐스팅합니다. malloc()은 void 포인터를 반환하기 때문입니다.
  5. malloc()이 성공했는지 항상 확인합니다! 만약 NULL을 반환하면 메모리가 부족합니다.
  6. numbers를 일반 배열처럼 사용할 수 있습니다.
  7. 메모리를 사용한 후에는 항상 free()를 호출합니다!

calloc() 함수

이제 calloc()을 만나보겠습니다. malloc()과는 달리, calloc()은 할당된 메모리를 모두 제로로 초기화합니다.

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

int main() {
int *numbers;
int size = 5;

numbers = (int*)calloc(size, sizeof(int));

if (numbers == NULL) {
printf("메모리 할당 실패!\n");
return 1;
}

for (int i = 0; i < size; i++) {
printf("numbers[%d] = %d\n", i, numbers[i]);
}

free(numbers);
return 0;
}

여기서의 주요 차이점은 다음과 같습니다:

  1. calloc()은 두 개의 인자를 받습니다: 요소의 수와 각 요소의 크기.
  2. 모든 요소는 제로로 초기화됩니다. 그래서 출력은 모두 제로입니다.

메모리 조정과 해제

때로는 우리의 파티가 예상보다 더 커지거나 작아질 수 있습니다. 이때 realloc()가 유용하게 쓰입니다!

realloc() 함수

realloc()은 마법사처럼 메모리 블록을 확장하거나 축소할 수 있습니다.

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

int main() {
int *numbers;
int size = 5;

numbers = (int*)malloc(size * sizeof(int));

if (numbers == NULL) {
printf("메모리 할당 실패!\n");
return 1;
}

for (int i = 0; i < size; i++) {
numbers[i] = i * 10;
printf("numbers[%d] = %d\n", i, numbers[i]);
}

// 배열을 확장합니다
size = 10;
numbers = (int*)realloc(numbers, size * sizeof(int));

if (numbers == NULL) {
printf("메모리 재할당 실패!\n");
return 1;
}

// 새로운 요소를 채우고 출력합니다
for (int i = 5; i < size; i++) {
numbers[i] = i * 10;
}

for (int i = 0; i < size; i++) {
printf("numbers[%d] = %d\n", i, numbers[i]);
}

free(numbers);
return 0;
}

이제 무엇이 일어나고 있는지 설명해 보겠습니다:

  1. 처음에 5개의 요소로 시작합니다.
  2. realloc()를 사용하여 배열을 10개의 요소로 확장합니다.
  3. realloc()는 원래 데이터를 유지하고 더 많은 공간을 제공합니다.
  4. 새로운 요소를 채우고 모든 요소를 출력합니다.

free() 함수

마지막으로, 우리의 메모리 관리 팀의 청소 부서인 free()를 소개합니다. 할당된 메모리를 사용한 후에는 항상 free()를 호출해야 합니다!

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

int main() {
int *numbers = (int*)malloc(5 * sizeof(int));

if (numbers == NULL) {
printf("메모리 할당 실패!\n");
return 1;
}

for (int i = 0; i < 5; i++) {
numbers[i] = i * 10;
printf("numbers[%d] = %d\n", i, numbers[i]);
}

free(numbers);  // 청소!
numbers = NULL; // 해제된 메모리를 사용하지 않도록 하는 좋은 관행

// 이제 'numbers'를 사용하려고 시도하는 것은 나쁜 생각입니다!

return 0;
}

기억해야 할 점:

  1. 할당된 메모리를 사용한 후에는 항상 free()를 호출합니다.
  2. 해제된 메모리를 사용하지 않도록 포인터를 NULL로 설정하는 것이 좋습니다.
  3. 다른 사람이 할당하지 않은 메모리를 free()하지 마세요.

이제 여러분은 메모리 관리의 고층 빌딩을 짓고, 파티 손님들을 위한 공간을 할당하고 조정하며, 파티가 끝난 후 청소하는 방법을 배웠습니다. 좋은 메모리 관리는 좋은 주인을 만드는 것과 같습니다 - 손님들이 충분한 공간을 가지고, 필요에 따라 공간을 조정하고, 파티가 끝난 후 철저히 청소하십시오!

이 개념들을 계속 연습하면, 여러분은 프로그램의 메모리를 주인장으로서 잘 관리할 수 있을 것입니다. 행복하게 코딩하시고, 메모리 누수가 없도록 하세요!

Credits: Image by storyset