C 언어 동적 메모리 할당 쉽게 이해하기 | malloc, free, valgrind 활용
메모리 할당과 해제: 프로그래머가 꼭 알아야 할 개념
컴퓨터에서 프로그램을 실행하면 메모리가 필요하다. 그런데 프로그램이 실행되는 동안 사용한 메모리를 제대로 해제하지 않으면 어떻게 될까? 제한된 메모리를 낭비하게 되어 결국 프로그램이 비효율적으로 동작하거나 심한 경우 시스템 전체에 영향을 줄 수 있다. 이런 문제를 방지하려면 동적 메모리 할당과 해제의 개념을 정확히 이해해야 한다.
이번 글에서는 malloc을 사용하여 메모리를 할당하는 방법과 free를 사용하여 할당된 메모리를 해제하는 방법을 배워보자. (ps. 메모리 누수와 버퍼 오버플로우 같은 문제를 방지하는 방법도 함께 기록해야지🧚🏻.)
메모리 할당과 해제란?
C 언어에서는 프로그램 실행 중 필요한 메모리를 동적으로 할당할 수 있다. 이때 사용하는 대표적인 함수가 malloc이다. 하지만 malloc을 사용하여 메모리를 할당한 후에는 반드시 free 함수를 사용하여 해당 메모리를 해제해야 한다.
메모리를 해제하지 않으면 프로그램이 종료될 때까지 해당 메모리는 사용 중으로 남게 된다. 이런 현상을 메모리 누수(memory leak) 라고 하며, 메모리 누수가 계속되면 시스템의 메모리가 부족해져 프로그램 실행이 느려지거나 비정상적으로 종료될 수도 있다.
메모리 할당과 해제의 기본 예제
아래 코드를 통해 malloc과 free의 기본적인 사용법을 살펴보자.
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int *ptr = malloc(5 * sizeof(int)); // 정수형 5개를 저장할 메모리 할당
if (ptr == NULL) {
printf("메모리 할당 실패!\n");
return 1;
}
for (int i = 0; i < 5; i++) {
ptr[i] = i * 10; // 배열에 값 저장
}
for (int i = 0; i < 5; i++) {
printf("ptr[%d] = %d\n", i, ptr[i]);
}
free(ptr); // 할당된 메모리 해제
return 0;
}
코드 설명
- malloc(5 * sizeof(int)): 정수형 데이터 5개를 저장할 수 있는 크기의 메모리를 동적으로 할당한다.
- ptr[i] = i * 10;: 할당된 메모리에 값을 저장한다. (i가 0이면 0, i가 1이면 10, i가 2이면 20)
- printf("ptr[%d] = %d\n", i, ptr[i]);: 배열의 인덱스와 값을 출력한다.
- 첫 번째 %d는 i 값을 출력한다.
- 두 번째 %d는 ptr[i] 값을 출력한다.
- free(ptr);: 사용이 끝난 메모리를 해제하여 메모리 누수를 방지한다.
실행 결과 예측
ptr[0] = 0
ptr[1] = 10
ptr[2] = 20
ptr[3] = 30
ptr[4] = 40
메모리 누수와 버퍼 오버플로우의 위험성
메모리 할당과 해제를 잘못하면 메모리 누수뿐만 아니라 버퍼 오버플로우(buffer overflow) 문제가 발생할 수도 있다. 이를 방지하기 위해 다음과 같은 점을 주의해야 한다.
1. 메모리 누수 (Memory Leak)
할당된 메모리를 해제하지 않으면 메모리 누수가 발생한다. 아래 예제를 살펴보자.
#include <stdlib.h>
void memory_leak() {
int *arr = malloc(10 * sizeof(int)); // 메모리 할당
arr[0] = 100; // 값 저장
// free(arr); 가 없기 때문에 메모리 누수 발생!
}
int main(void) {
memory_leak();
return 0;
}
위 코드에서 malloc을 사용하여 할당한 메모리를 free 하지 않으면 프로그램이 종료될 때까지 해당 메모리가 반환되지 않는다. 이를 방지하려면 free(arr);를 추가해야 한다.
2. main() 함수의 역할과 필요성
- C 프로그램은 모든 실행이 main() 함수에서 시작된다.
- main()에서 memory_leak();을 호출하지 않으면 해당 함수가 실행되지 않기 때문에 메모리 할당도 이루어지지 않는다.
- return 0;은 프로그램이 정상적으로 종료되었음을 운영체제에 알리는 역할을 한다.
3. 버퍼 오버플로우 (Buffer Overflow)
버퍼 오버플로우는 할당된 메모리보다 더 많은 데이터를 저장하려고 할 때 발생한다.
#include <stdlib.h>
void buffer_overflow() {
int *arr = malloc(10 * sizeof(int)); // 10개의 정수 크기만큼 할당
arr[10] = 50; // 11번째 인덱스에 접근 -> 오류 발생
free(arr);
}
int main(void) {
buffer_overflow();
return 0;
}
위 코드에서 arr은 0~9까지 10개의 정수를 저장할 수 있지만 arr[10] = 50;을 실행하면 11번째 위치에 접근하게 되어 버퍼 오버플로우가 발생한다. 하지만 이는 문자열에서 널 종단 문자(null terminator)에 접근하는 것과는 다르다. arr은 int 배열이므로 단순히 배열 범위를 넘어선 메모리에 접근하는 것이며, 이로 인해 예상치 못한 값이 저장되거나 프로그램이 충돌할 수도 있다.
valgrind를 활용한 메모리 검사
C 언어에서는 valgrind라는 도구를 사용하여 메모리 관련 오류를 쉽게 찾을 수 있다. valgrind를 사용하면 메모리 누수, 버퍼 오버플로우 같은 문제를 찾아서 해결할 수 있다.
valgrind 사용법
터미널에서 다음과 같은 명령어를 실행하면 프로그램의 메모리 관련 문제를 검사할 수 있다.
valgrind ./filename
혹은 더 자세한 메모리 검사를 원한다면 다음 명령어를 사용할 수 있다.
valgrind --leak-check=full ./filename
이 명령어를 실행하면 할당된 메모리와 해제된 메모리를 검사하고, 문제가 발생한 부분을 상세히 알려준다.
메모리를 올바르게 관리하는 것은 안정적인 프로그램을 작성하는 데 매우 중요하다. malloc을 사용하여 메모리를 동적으로 할당한 후에는 반드시 free를 사용하여 메모리를 해제해야 한다. 또한, 배열의 크기를 초과하여 접근하지 않도록 주의해야 하며, valgrind 같은 도구를 활용하면 코드에서 발생하는 메모리 오류를 쉽게 찾을 수 있다는 점 기억하자(:
'IT' 카테고리의 다른 글
C에서 사용자 입력을 받아 파일에 저장하는 방법 - scanf, fopen, fprintf 사용법 (0) | 2025.04.02 |
---|---|
스택과 힙의 차이점 이해하기: 포인터를 이용한 메모리 교환 방법 (0) | 2025.03.30 |
C 언어에서 문자열 복사하는 방법: malloc과 for문을 활용한 정확한 복사 (0) | 2025.03.30 |
C 언어에서 string이 없는 이유 – 문자열과 포인터의 관계 (0) | 2025.03.30 |
인터넷 속도 계산법 : 1Gbps, 100Mbps 다운로드 속도 비교 (0) | 2025.03.28 |
댓글
이 글 공유하기
다른 글
-
C에서 사용자 입력을 받아 파일에 저장하는 방법 - scanf, fopen, fprintf 사용법
C에서 사용자 입력을 받아 파일에 저장하는 방법 - scanf, fopen, fprintf 사용법
2025.04.02 -
스택과 힙의 차이점 이해하기: 포인터를 이용한 메모리 교환 방법
스택과 힙의 차이점 이해하기: 포인터를 이용한 메모리 교환 방법
2025.03.30 -
C 언어에서 문자열 복사하는 방법: malloc과 for문을 활용한 정확한 복사
C 언어에서 문자열 복사하는 방법: malloc과 for문을 활용한 정확한 복사
2025.03.30 -
C 언어에서 string이 없는 이유 – 문자열과 포인터의 관계
C 언어에서 string이 없는 이유 – 문자열과 포인터의 관계
2025.03.30