C 프로그래머들은 런타임에 추가 가상 메모리가 필요할 때, 저수준의 mmap과 munmap 함수를 사용하는 것보다 동적 메모리 할당자(dynamic memory allocator)를 사용하는 것이 더 편리하고 이식성이 좋습니다.

malloc 패키지malloc()으로 블록 할당, free()로 블록 해제new와 delete도 유사#include <stdlib.h>
void *malloc(size_t size);
// 성공 시: 할당된 블록에 대한 포인터 반환
// 실패 시: NULL 반환
특징:
size 바이트의 메모리 블록에 대한 포인터 반환#include <unistd.h>
void *sbrk(intptr_t incr);
// 성공 시: 이전 brk 포인터 반환
// 실패 시: -1 반환
incr를 추가하여 힙을 확장하거나 축소incr가 0이면 현재 brk 값 반환incr는 합법적이지만 까다로움#include <stdlib.h>
void free(void *ptr);
// 반환값: 없음
주의사항:
ptr 인자는 malloc, calloc, realloc에서 얻은 할당된 블록의 시작을 가리켜야 함free의 동작이 정의되지 않음
(a) p1 = malloc(4*sizeof(int)) → 4워드 블록 할당
(b) p2 = malloc(5*sizeof(int)) → 6워드 블록 할당 (정렬을 위해 패딩)
(c) p3 = malloc(6*sizeof(int)) → 6워드 블록 할당
(d) free(p2) → p2 블록 해제
(e) p4 = malloc(2*sizeof(int)) → 해제된 블록의 일부 재사용
#define MAXN 15213
int array[MAXN];
int main() {
int i, n;
scanf("%d", &n);
if (n > MAXN)
app_error("Input file too big");
for (i = 0; i < n; i++)
scanf("%d", &array[i]);
exit(0);
}
문제점:
MAXN 값이 임의적이고 실제 가상 메모리 양과 무관MAXN보다 큰 파일을 읽으려면 프로그램을 다시 컴파일해야 함int main() {
int *array, i, n;
scanf("%d", &n);
array = (int *)Malloc(n * sizeof(int));
for (i = 0; i < n; i++)
scanf("%d", &array[i]);
free(array);
exit(0);
}
장점:
Uk = max(i≤k) Pi / HkPi: 현재 할당된 블록들의 페이로드 합계Hk: 현재 힙 크기

0x00000018 | 0x1 = 0x000000190x00000028 | 0x0 = 0x00000028sbrk 함수로 커널에 추가 힙 메모리 요청

다음 블록과의 병합: 간단하고 효율적
이전 블록과의 병합: 문제 발생
free 호출이 힙의 크기에 선형 시간 요구할당자가 현재 블록을 해제할 때 존재할 수 있는 모든 경우:
세 블록 모두를 병합하여 하나의 자유 블록 형성
이전 블록의 헤더와 다음 블록의 푸터를 세 블록의 결합된 크기로 업데이트
성능: 각 경우에서 병합은 상수 시간에 수행
malloc과 free를 반복적으로 호출하여 그래프 노드를 동적으로 생성하고 파괴하고, 각 그래프 노드가 몇 워드의 메모리만 필요하다면, 헤더와 푸터가 각 할당된 블록의 절반을 소비하게 됩니다.
static void *coalesce(void *bp) {
size_t prev_alloc = GET_ALLOC(FTRP(PREV_BLKP(bp)));
size_t next_alloc = GET_ALLOC(HDRP(NEXT_BLKP(bp)));
size_t size = GET_SIZE(HDRP(bp));
if (prev_alloc && next_alloc) { /* 경우 1 */
return bp;
}
else if (prev_alloc && !next_alloc) { /* 경우 2 */
size += GET_SIZE(HDRP(NEXT_BLKP(bp)));
PUT(HDRP(bp), PACK(size, 0));
PUT(FTRP(bp), PACK(size, 0));
}
else if (!prev_alloc && next_alloc) { /* 경우 3 */
size += GET_SIZE(HDRP(PREV_BLKP(bp)));
PUT(FTRP(bp), PACK(size, 0));
PUT(HDRP(PREV_BLKP(bp)), PACK(size, 0));
bp = PREV_BLKP(bp);
}
else { /* 경우 4 */
size += GET_SIZE(HDRP(PREV_BLKP(bp))) +
GET_SIZE(FTRP(NEXT_BLKP(bp)));
PUT(HDRP(PREV_BLKP(bp)), PACK(size, 0));
PUT(FTRP(NEXT_BLKP(bp)), PACK(size, 0));
bp = PREV_BLKP(bp);
}
return bp;
}
1 /* Private global variables */
2 static char *mem_heap; /* Points to first byte of heap */
3 static char *mem_brk; /* Points to last byte of heap plus 1 */
4 static char *mem_max_addr; /* Max legal heap addr plus 1*/
5
6 /*
7 * mem_init - Initialize the memory system model
8 */
9 void mem_init(void)
10 {
11 mem_heap = (char *)Malloc(MAX_HEAP);
12 mem_brk = (char *)mem_heap;
13 mem_max_addr = (char *)(mem_heap + MAX_HEAP);
14 }
15
16 /*
17 * mem_sbrk - Simple model of the sbrk function. Extends the heap
18 * by incr bytes and returns the start address of the new area. In
19 * this model, the heap cannot be shrunk.
20 */
21 void *mem_sbrk(int incr)
22 {
23 char *old_brk = mem_brk;
24
25 if ( (incr < 0) || ((mem_brk + incr) > mem_max_addr)) {
26 errno = ENOMEM;
27 fprintf(stderr, "ERROR: mem_sbrk failed. Ran out of memory...\n");
28 return (void *)-1;
29 }
30 mem_brk += incr;
31 return (void *)old_brk;
32 }
static char *mem_heap; /* 힙의 첫 번째 바이트를 가리킴 */
static char *mem_brk; /* 힙의 마지막 바이트 + 1을 가리킴 */
static char *mem_max_addr; /* 최대 합법적 힙 주소 + 1 */
mem_heap = (char *)Malloc(MAX_HEAP): 힙의 시작 주소 설정mem_brk = (char *)mem_heap: 초기 brk 포인터 설정mem_max_addr = (char *)(mem_heap + MAX_HEAP): 최대 주소 설정old_brk = mem_brk: 현재 brk 값 저장incr < 0 또는 (mem_brk + incr) > mem_max_addrmem_brk += incr: brk 포인터 업데이트return (void *)old_brk: 이전 brk 값 반환mm_init(): 할당자 초기화mm_malloc(): 블록 할당mm_free(): 블록 해제
[패딩] → [프롤로그 블록] → [정규 블록들] → [에필로그 블록]
#define WSIZE 4 /* 워드 및 헤더/푸터 크기 */
#define DSIZE 8 /* 더블 워드 크기 */
#define CHUNKSIZE (1<<12) /* 힙 확장 크기 */
#define PACK(size, alloc) ((size) | (alloc))
#define GET(p) (*(unsigned int *)(p))
#define PUT(p, val) (*(unsigned int *)(p) = (val))
#define GET_SIZE(p) (GET(p) & ~0x7)
#define GET_ALLOC(p) (GET(p) & 0x1)
#define HDRP(bp) ((char *)(bp) - WSIZE)
#define FTRP(bp) ((char *)(bp) + GET_SIZE(HDRP(bp)) - DSIZE)
#define NEXT_BLKP(bp) ((char *)(bp) + GET_SIZE(((char *)(bp) - WSIZE)))
#define PREV_BLKP(bp) ((char *)(bp) - GET_SIZE(((char *)(bp) - DSIZE)))
int mm_init(void) {
if ((heap_listp = mem_sbrk(4*WSIZE)) == (void *)-1)
return -1;
PUT(heap_listp, 0); /* 정렬 패딩 */
PUT(heap_listp + (1*WSIZE), PACK(DSIZE, 1)); /* 프롤로그 헤더 */
PUT(heap_listp + (2*WSIZE), PACK(DSIZE, 1)); /* 프롤로그 푸터 */
PUT(heap_listp + (3*WSIZE), PACK(0, 1)); /* 에필로그 헤더 */
heap_listp += (2*WSIZE);
if (extend_heap(CHUNKSIZE/WSIZE) == NULL)
return -1;
return 0;
}
static void *extend_heap(size_t words) {
char *bp;
size_t size;
size = (words % 2) ? (words+1) * WSIZE : words * WSIZE;
if ((long)(bp = mem_sbrk(size)) == -1)
return NULL;
PUT(HDRP(bp), PACK(size, 0)); /* 가용 블록 헤더 */
PUT(FTRP(bp), PACK(size, 0)); /* 가용 블록 푸터 */
PUT(HDRP(NEXT_BLKP(bp)), PACK(0, 1)); /* 새 에필로그 헤더 */
return coalesce(bp);
}
void mm_free(void *bp) {
size_t size = GET_SIZE(HDRP(bp));
PUT(HDRP(bp), PACK(size, 0));
PUT(FTRP(bp), PACK(size, 0));
coalesce(bp);
}
static void *coalesce(void *bp) {
size_t prev_alloc = GET_ALLOC(FTRP(PREV_BLKP(bp)));
size_t next_alloc = GET_ALLOC(HDRP(NEXT_BLKP(bp)));
size_t size = GET_SIZE(HDRP(bp));
if (prev_alloc && next_alloc) { /* 경우 1 */
return bp;
}
else if (prev_alloc && !next_alloc) { /* 경우 2 */
size += GET_SIZE(HDRP(NEXT_BLKP(bp)));
PUT(HDRP(bp), PACK(size, 0));
PUT(FTRP(bp), PACK(size, 0));
}
else if (!prev_alloc && next_alloc) { /* 경우 3 */
size += GET_SIZE(HDRP(PREV_BLKP(bp)));
PUT(FTRP(bp), PACK(size, 0));
PUT(HDRP(PREV_BLKP(bp)), PACK(size, 0));
bp = PREV_BLKP(bp);
}
else { /* 경우 4 */
size += GET_SIZE(HDRP(PREV_BLKP(bp))) +
GET_SIZE(FTRP(NEXT_BLKP(bp)));
PUT(HDRP(PREV_BLKP(bp)), PACK(size, 0));
PUT(FTRP(NEXT_BLKP(bp)), PACK(size, 0));
bp = PREV_BLKP(bp);
}
return bp;
}
void *mm_malloc(size_t size) {
size_t asize; /* 조정된 블록 크기 */
size_t extendsize; /* 적합한 블록이 없을 때 확장할 크기 */
char *bp;
if (size == 0)
return NULL;
/* 블록 크기를 오버헤드와 정렬 요구사항을 포함하도록 조정 */
if (size <= DSIZE)
asize = 2*DSIZE;
else
asize = DSIZE * ((size + (DSIZE) + (DSIZE-1)) / DSIZE);
/* 가용 리스트에서 적합한 블록 검색 */
if ((bp = find_fit(asize)) != NULL) {
place(bp, asize);
return bp;
}
/* 적합한 블록을 찾지 못함. 더 많은 메모리를 얻고 블록 배치 */
extendsize = MAX(asize, CHUNKSIZE);
if ((bp = extend_heap(extendsize/WSIZE)) == NULL)
return NULL;
place(bp, asize);
return bp;
}

2의 거듭제곱으로 분할:
{1}, {2}, {3, 4}, {5–8}, ..., {1,025–2,048}, {2,049–4,096}, {4,097–∞}
작은 블록을 개별 크기 클래스로 할당:
{1}, {2}, {3}, ..., {1,023}, {1,024}, {1,025–2,048}, {2,049–4,096}, {4,097–∞}
장점:
단점:
작동 방식:
1. 요청의 크기 클래스 결정
2. 적절한 가용 리스트에서 최초 적합 검색
3. 적합한 블록을 찾으면 (선택적으로) 분할하고 조각을 적절한 가용 리스트에 삽입
4. 적합한 블록을 찾지 못하면 다음 큰 크기 클래스의 가용 리스트 검색
5. 블록 해제 시 병합하고 적절한 가용 리스트에 배치
장점:
작동 방식:
1. 요청된 블록 크기를 가장 가까운 2의 거듭제곱으로 반올림
2. 크기 2^k의 블록 할당을 위해 k ≤ j ≤ m인 첫 번째 사용 가능한 크기 2^j 블록 찾기
3. j = k이면 완료, 그렇지 않으면 j = k가 될 때까지 블록을 반으로 분할
4. 분할 과정에서 남은 절반(버디)을 적절한 가용 리스트에 배치
5. 크기 2^k의 블록 해제 시 가용 버디와 계속 병합
버디 주소 계산:
xxx...x00000xxx...x10000장점:

typedef void *ptr;
ptr isPtr(ptr p); // p가 할당된 블록의 어떤 워드를 가리키면 해당 블록의 시작 포인터 반환, 아니면 NULL
int blockMarked(ptr b); // 블록 b가 이미 마크되었으면 true 반환
int blockAllocated(ptr b); // 블록 b가 할당되었으면 true 반환
void markBlock(ptr b); // 블록 b를 마크
int length(ptr b); // 블록 b의 길이를 워드 단위로 반환 (헤더 제외)
void unmarkBlock(ptr b); // 블록 b의 상태를 마크됨에서 마크되지 않음으로 변경
ptr nextBlock(ptr b); // 힙에서 블록 b의 후속자 반환
void mark(ptr p) {
if ((b = isPtr(p)) == NULL)
return;
if (blockMarked(b))
return;
markBlock(b);
len = length(b);
for (i=0; i < len; i++)
mark(b[i]);
return;
}
void sweep(ptr b, ptr end) {
while (b < end) {
if (blockMarked(b))
unmarkBlock(b);
else if (blockAllocated(b))
free(b);
b = nextBlock(b);
}
return;
}


전형적인 scanf 버그:
// 올바른 방법
scanf("%d", &val)
// 잘못된 방법
scanf("%d", val) // val의 내용을 주소로 해석하여 해당 위치에 쓰기 시도
잘못된 가정:
int *y = (int *)Malloc(n * sizeof(int));
// y가 0으로 초기화되었다고 잘못 가정
for (i = 0; i < n; i++)
for (j = 0; j < n; j++)
y[i] += A[i][j] * x[j]; // 초기화되지 않은 값에 더하기
해결책:
// 명시적으로 0으로 초기화
for (i = 0; i < n; i++)
y[i] = 0;
// 또는 calloc 사용
int *y = (int *)calloc(n, sizeof(int));
void bufoverflow() {
char buf[64];
gets(buf); // 스택 버퍼 오버플로우 버그
return;
}
해결책:
void bufoverflow() {
char buf[64];
fgets(buf, sizeof(buf), stdin); // 입력 문자열 크기 제한
return;
}
/* Create an nxm array */
int **makeArray2(int n, int m)
{
int i;
int **A = (int **)Malloc(n * sizeof(int *)); // 잘못됨: sizeof(int *)여야 함
for (i = 0; i <= n; i++)
A[i] = (int *)Malloc(m * sizeof(int));
return A;
}
문제점:
for (i = 0; i <= n; i++) // 잘못됨: i < n이어야 함
A[i] = (int *)Malloc(m * sizeof(int));
*size--;
1 int *binheapDelete(int **binheap, int *size)
2 {
3 int *packet = binheap[0];
4
5 binheap[0] = binheap[*size - 1];
6 *size--; // 잘못됨: 포인터 자체를 감소시킴
7 heapify(binheap, *size, 0);
8 return(packet);
9 }
올바른 방법:
(*size)--; // 포인터가 가리키는 정수 값을 감소시킴
p += sizeof(int); // 잘못됨: 4바이트씩 증가
올바른 방법:
p++; // 포인터가 가리키는 객체 크기만큼 증가
int *stackref() {
int val;
return &val; // 스택 프레임이 팝된 후 유효하지 않은 변수 참조
}
free(x);
y = (int *)Malloc(m * sizeof(int));
for (i = 0; i < m; i++)
y[i] = x[i]++; // 해제된 블록의 데이터 참조
void leak(int n) {
int *x = (int *)Malloc(n * sizeof(int));
return; // x는 이 시점에서 가비지
}
문제점:
leak이 자주 호출되면 힙이 가비지로 점진적으로 채워짐DMA의 동작 방식