매크로 함수를 이욯하면, 성능최적화와 코드 간결화를 얻을 수 있다.
int , float, double 등 어떤 타입이든 입자로 넣을 수 있다. (C의 함수는 타입을 정확히 맞춰야하기 때문에 간편함 !)#define SQUARE(x) x*x // 잘못된 매크로
int y = SQUARE(1+2); // 1+2*1+2 = 5, 예상은 9인데...
위와 같은 경우를 막으려면
#define SQUARE(x) ((x)*(x)) // 이렇게 괄호로 묶어줘야 함
요약하면
→ 함수 호출 부담을 없애고, 짧은 코드를 편하게 쓰기 위해 매크로를 쓴다.
(단, 신중하게 써야 하고 요즘은 inline 함수가 매크로를 대체하는 경우도 많다.)
1. Alligment 상수
#define ALIGNMENT 8
32비트의 운영체제를 기반으로 malloc을 구현하기 떄문에 기본 ALIGNMENT를 8바이트로 명시해줬다.
2. ALIGN, SIZE_T_SIZE 매크로 (블록 정렬)
#define ALIGN(size) (((size) + (ALIGNMENT - 1)) & ~0x7
#define SIZE_T_SIZE (ALIGN(sizeof(size_t)))
블록의 SIZE가 들어오면 해당 SIZE를 8의 배수를 올림 연산을 해주는 매크로이다.
~0x7 = 0xFFFFFFF8 (하위 3비트 0 → AND연산 하면 하위 비트 날리고 8의 배수 됨)
비트 마스킹을 활용한 빠른 나머지 제거 방식
size_t 크기를 8의 배수로 올린 값을 반영함(정렬 안전성 확보) 정렬 깨지면 성능 문제 + 크래시 위험
3. WSIZE, DSIZE, CHUNKSIZE 상수 선언
#define WSIZE 4 /* word size (byte)*/
#define DSIZE 8 /* double word SIZE (byte) */
#define CHUNKSIZE (1<<12) /* 초기 가용 블록과 힙 확장을 위한 크기 (byte)*/
WSIZE는 word 한개의 사이즈인 4바이트를 선언하였고,
DSIZE는 double word 사이즈로 word 2개의 사이즈인 8바이트 선언
CHUNKSIZE는 쉬프트 연산을 통해 2^12 값인 4096을 넣어주었다.
4. MAX 매크로
#define MAX(x, y) ((x) > (y) ? (x) : (y)) // 둘중 더 큰 값을 반환하는 max 매크로
삼항 연산자를 통하여 MAX함수를 매크로로 구현하였다. 매크로를 구현할 때 주의점을 항상 인지하며, 괄호를 생활화 하자..!
5. PACK 매크로
#define PACK(size, alloc) ((size) | (alloc)) /* 헤더, 푸터를 만들기 위한 매크로*/
size(블록 크기)와 할당 여부(alloc)를 하나의 4바이트 값으로 합치는 매크로이다.
size는 보통 8의 배수야 (8바이트 정렬 때문) → 하위 3비트(000)가 무조건 비어있다. alloc은 0 또는 1 (0: free, 1: allocated).
이 둘을 OR 연산으로 합치면 상위 비트는 크기 정보 맨 하위 비트는 할당 여부 정보가 들어감.
해당 매크로로 header와 footer 구현 가능
예시 -> PACK(24, 1) → 0x18 | 0x1 → 0x19
6. GET, PUT 매크로
#define GET(p) (*(unsigned int *)(p)) // 해당 주소에 저장된 4바이트의 값을 읽어오는 것
#define PUT(p, val) (*(unsigned *)(p) = (val)) //해당 주소에 4바이트의 값을 써주는 것
GET(p) : 주소 P가 가리키는 메모리에서 4바이트(32비트)를 읽어옮
PUT(p, val) : 주소 P에 4바이트(32비트) 값을 val로 써줌
7. GET_SIZE, GET_ALLOC 매크로
#define GET_SIZE(p) (GET(p) & ~0x7) // 하위 3비트 무시 (사이즈만)
#define GET_ALLOC(p) (GET(p) & 0X1) // 하위 1비트에 1을 넣고 and연산 (할당 여부만)
GET_SIZE(p) : p가 가리키는 헤더/푸터에서 블록 크기만 뽑아냄.
0X7 == 0b111 -> 하위 3비트가 모두 1~0x7 == 0xffffff...ff8 -> 하위 3비트만 0, 나머지는 1AND연산을 통해 할당플래그(맨 하위 비트)랑 padding bit들을 제거하고, 진짜 size만 가져옴.GET_ALLOC(p) : p가 가리키는 헤더/푸터에서 할당 여부만 뽑아냄.
0X1)만 AND 연산해서 0 (free) / 1 (allocated) 을 판별함.8. HDRP, FTRP 매크로
#define HDRP(bp) ((char *)(bp) - WSIZE) // payload 기준 4바이트(1워드) 앞으로 가서 헤더 찾기
#define FTRP(bp) ((char *)(bp) + GET_SIZE(HDRP(bp)) - DSIZE)// payload 기준 블록크기만큼 뒤로 가서 푸터 찾기
HFTP(bp) : bp (payload 시작주소) 에서 4바이트(1워드) 앞으로 이동해서 헤더의 주소를 찾는다.
[헤더][payload][푸터] 형태이다.payload 바로 앞에 헤더가 4바이트로 있음.WSIZE는 4바이트 (word size)로 정의되어 있다고 가정함.FTRP(bp) : bp(payload 시작 주소) 기준으로 푸터의 주소를 찾아줌.
GET_SIZE(HDRP(bp)) -> 이 블록의 전체 크기.
거기서 DSIZE(8바이트)를 빼줌.
왜 8바이트를 빼냐?
- payload 앞쪽에 4바이트 헤더
- payload 끝나고 나서 바로 4바이트 푸터
- 총 8바이트니까!
9. NEXT_BLKP, PREV_BLKP 매크로
#define NEXT_BLKP(bp) ((char *)(bp) + GET_SIZE(((char *)(bp) - WSIZE)))
#define PREV_BLKP(bp) ((char *)(bp) - GET_SIZE(((char *)(bp) - DSIZE)))
NEXT_BLKP(bp) : 현재 블록 bp 기준으로 다음 블록의 payload 주소를 구함.
현재 payload에서 현재 블록의 헤더를 찾고 (bp - 4)
GET_SIZE(헤더)로 이 블록의 크기를 얻어
그만큼 오른쪽으로 이동하면 다음 블록의 payload가 나옴.
PREV_BLKP(bp) : 현재 블록 bp 기준으로 이전 블록의 payload 주소를 구함.
bp - 8 하면 이전 블록의 푸터 주소.
GET_SIZE(푸터)를 읽어오면 이전 블록의 크기.
그 크기만큼 왼쪽으로 이동하면 이전 블록의 payload 주소를 알 수 있다.
/* 32비트 운영체제를 사용하기 때문에 8바이트 사용...*/
#define ALIGNMENT 8
/* rounds up to the nearest multiple of ALIGNMENT */
/* size를 8의 배수로 올림 연산을 해주는 것이다.*/
/* (size + 7) : 8로 나누어 떨어지게 만들기 위해 +7
~0x7 = 0xFFFFFFF8 (하위 3비트 0 → AND 하면 하위 비트 날리고 8의 배수 됨)
비트 마스킹을 활용한 빠른 나머지 제거 방식 */
#define ALIGN(size) (((size) + (ALIGNMENT - 1)) & ~0x7)
/* size_t 크기를 8의 배수로 올린 값 (정렬 안전성 확보) */
/*정렬 깨지면 성능 문제 + 크래시 위험*/
#define SIZE_T_SIZE (ALIGN(sizeof(size_t)))
/* Basic constants and macros */
#define WSIZE 4 /* word size (byte)*/
#define DSIZE 8 /* double word SIZE (byte) */
#define CHUNKSIZE (1<<12) /* 초기 가용 블록과 힙 확장을 위한 크기 (byte)*/
#define MAX(x, y) ((x) > (y) ? (x) : (y)) // 둘중 더 큰 값을 반환하는 max 매크로
/*Pack a size and allocated bit into a word */
#define PACK(size, alloc) ((size) | (alloc)) /* 헤더, 푸터를 만들기 위한 매크로*/
/*Read and write a word at address p */
/*여기서 p는 메모리블록의 헤더 푸터를 가르킨다.*/
/*헤더와 푸터를 조작하는 매크로*/
#define GET(p) (*(unsigned int *)(p)) // 해당 주소에 저장된 4바이트의 값을 읽어오는 것
#define PUT(p, val) (*(unsigned *)(p) = (val)) //해당 주소에 4바이트의 값을 써주는 것
/* Read the size and allocated fields from address p */
#define GET_SIZE(p) (GET(p) & ~0x7) // 하위 3비트 무시 (사이즈만)
#define GET_ALLOC(p) (GET(p) & 0X1) // 하위 1비트에 1을 넣고 and연산 (할당 여부만)
/* Given block ptr bp, compute address of its header and footer */
#define HDRP(bp) ((char *)(bp) - WSIZE) // payload 기준 4바이트(1워드) 앞으로 가서 헤더 찾기
#define FTRP(bp) ((char *)(bp) + GET_SIZE(HDRP(bp)) - DSIZE)// payload 기준 블록크기만큼 뒤로 가서 푸터 찾기
/* Given bloack ptr bp, compute address of next and previous blocks */
#define NEXT_BLKP(bp) ((char *)(bp) + GET_SIZE(((char *)(bp) - WSIZE)))
#define PREV_BLKP(bp) ((char *)(bp) - GET_SIZE(((char *)(bp) - DSIZE)))