C 언어에서 프로그래머는 직접 메모리를 관리합니다. 배열, 구조체 및 포인터의 배치, 해제 및 사용은 수동으로 제어됩니다. 동적 메모리 할당 메커니즘은 1970년대부터 C에 존재하며 malloc, calloc, realloc, free와 같은 표준 라이브러리의 특별한 함수를 통해 구현됩니다. 이러한 접근 방식은 성능과 유연성을 제공하지만 주의가 필요합니다.
메모리 작업 중 오류는 메모리 누수, 다른 데이터 손상, 프로그램 충돌 또는 보안 취약점으로 이어질 수 있습니다. 오류는 종종 free 호출을 잊거나, 배열의 경계를 초과하거나, 포인터 형 변환이 잘못되거나, 메모리를 이중으로 해제하는 경우 발생합니다. 구조체의 경우도 유사하지만 중첩된 동적 필드를 정리하는 것을 잊는 위험이 추가됩니다.
오류를 최소화하기 위해, 엄격하게 구조화된 코드를 개발하고, 모든 메모리 할당과 해제를 추적하며, 해제된 포인터를 사용하지 않고 할당된 배열의 크기를 추적하는 것이 좋습니다. 정적 분석 도구, 마지막 검사(valgrind, sanitizers)를 사용하고 malloc을 호출한 사람이 메모리를 해제하는 규칙을 준수하는 것이 중요합니다.
코드 예시:
#include <stdio.h> #include <stdlib.h> typedef struct { int *arr; size_t size; } ArrayWrapper; ArrayWrapper *create(size_t n) { ArrayWrapper *aw = malloc(sizeof(ArrayWrapper)); if (!aw) return NULL; aw->arr = malloc(sizeof(int) * n); if (!aw->arr) { free(aw); return NULL; } aw->size = n; return aw; } void destroy(ArrayWrapper *aw) { if (aw) { free(aw->arr); free(aw); } }
주요 특징:
널 포인터에 대한 메모리 해제를 할 경우 (free(NULL)) 어떤 일이 발생합니까?
C 표준에 따르면, 널 포인터에 대한 free 호출은 안전하며 아무 일도 발생하지 않습니다. 이는 메모리를 해제하는 책임을 전가하는 데 편리합니다.
free 호출 후 메모리를 사용할 수 있습니까?
아니요, 메모리를 해제한 후(사용 후 해제)는 고전적인 오류입니다. 데이터가 변경되거나 영역이 다른 프로세스에 할당될 수 있습니다. free 후에는 항상 포인터를 null로 설정해야 합니다.
int *ptr = malloc(10); free(ptr); ptr = NULL; // 안전함
프로그램 종료 전에 할당된 메모리를 반드시 해제해야 합니까?
기술적으로는 필수는 아니지만 — 프로그램이 종료될 때 OS가 모든 리소스를 해제합니다. 그러나 메모리 해제를 무시하는 것은 안티패턴이며, 이는 디버깅을 어렵게 하고 크고 오래 실행되는 프로그램에서 오류를 유발합니다.
개발자가 함수 내에서 동적 배열을 생성하고 정리하는 것을 잊습니다:
장점:
단점:
모든 코드는 정적 분석 도구를 통해 검사를 받고, free 후 모든 포인터가 null로 설정되며, 모든 malloc은 free와 연결됩니다:
장점:
단점: