동적할당 / 자료구조
메모리
메모리는 프로그램이 실행될 때 데이터를 잠시 보관하는 저장 장소입니다. 아주 쉽게 말하면 “컴퓨터의 책장”이라고 생각하세요.
간단한 비유 (책장):
각 칸(서랍)은 1바이트(또는 여러 바이트)의 공간을 가지고 있고, 칸마다 번호(주소)가 붙어 있습니다.
예시 ASCII 그림:
책장(메모리)
[0x100] [0x101] [0x102] [0x103] [0x104] [0x105] ... <- 각각의 칸이 주소를 가짐
0 0 42 0 ? ?
- 주소(address): 각 칸의 번호입니다. 주소를 알고 있으면 그 칸(메모리)에 들어있는 값을 읽거나 쓸 수 있습니다.
- 포인터(pointer): 주소를 저장하는 변수입니다. 즉, 어떤 칸을 가리키는 종이(주소)를 들고 있다고 생각하면 됩니다.
아주 짧은 C 예제(개념 확인용):
int a = 10; // a는 메모리의 어떤 칸에 10을 저장
int *p = &a; // p는 a가 있는 칸의 주소를 저장
// &a는 a의 주소, *p는 p가 가리키는 칸의 값
printf("주소: %p, 값: %d\n", (void*)p, *p);
위 코드를 실행하면 p(주소)와 그 주소에 들어있는 값(10)을 볼 수 있습니다. 이 정도 개념만 알면 포인터와 메모리의 기본을 이해하는 데 충분합니다.
포인터
포인터는 메모리의 주소를 저장하는 변수입니다. 동적할당과 메모리 관리에서 매우 중요한 역할을 합니다.
포인터 타입은 type관련없이 8byte 자료형입니다.
int a = 10;
int *p = &a; // p는 a의 주소를 저장
printf("%d", *p); // p가 가리키는 곳의 값(10) 출력
동적할당된 메모리는 포인터로만 접근할 수 있습니다.
지역변수와 동적할당
지역변수는 함수가 끝나면 자동으로 소멸되지만, 동적할당된 메모리는 직접 해제하기 전까지 남아있습니다.
void foo() {
int x = 10; // 지역변수, 함수 끝나면 사라짐
int *p = malloc(sizeof(int)); // 동적할당, 함수 끝나도 남아있음
*p = 20;
// ...
free(p); // 직접 해제 필요
}
할당 해제
동적할당한 메모리는 반드시 free로 해제해야 합니다. 해제하지 않으면 메모리 누수가 발생합니다.
int *p = malloc(sizeof(int) * 10);
// ... 사용
free(p); // 메모리 반환
메모리 누수
메모리 누수란, 할당한 메모리를 해제하지 않아 프로그램이 점점 더 많은 메모리를 차지하게 되는 현상입니다. 반복적으로 동적할당만 하고 해제하지 않으면 프로그램이 느려지거나 비정상 종료될 수 있습니다.
for(int i=0; i<1000; i++) {
int *p = malloc(sizeof(int) * 100);
// ... 사용
// free(p); // 해제하지 않으면 누수 발생
}
실제 프로그램에서는 동적할당 후 반드시 free로 해제하는 습관이 중요합니다.