직원 레코드(Rec)를 힙에 만들고, "ID 인덱스"(by_id)와 "이름 순 인덱스"(by_name) 두 배열로 관리한다.
두 인덱스는 정렬만 다를 뿐, 결국 "같은 Rec 객체들"을 가리킨다(별칭, aliasing).
조회/출력 후 프로그램 끝에서 전부 정리한다.
이름부터 더블프리지만!!
일단 gdb ㄱㄱ
일단 돌려보니 SIGABRT로 죽어서 bt부터 찍어봄
free(): double free detected in tcache 2
Program received signal SIGABRT, Aborted.
0x5555555592e0SIGABRT 상태에서 n을 더 치면 프로세스가 죽어버려서 d 같은 지역변수를 못 봄..
그래서 일단 bt로 어디에서 터졌는지 확인하고, 실제 변수 값은 프로그램이 살아있을 때 확인하기로 함
directory_free에 브레이크 찍고 들어가봄

for (i...) { // 루프 1
free(d->by_id[i]->name);
free(d->by_id[i]); // ← Rec 4개를 전부 free
}
for (i...) { // 루프 2
free(d->by_name[i]); // ← 같은 Rec 4개를 또 free → 터짐
}
by_id[i],by_name[i] → 같은 rec 객체를 가리킴
루프 1에서 전부 free하면 루프2의 by_name[i] 는 이미 프리된 포인터인데 같은 주소를 다시 free()하고 있음
실제로 같은 객체를 가리키는지 확인하기 위해 @4를 사용해서 배열의 주소를 한 번에 4개씩 확인

순서만 다르고 주소 4개가 똑같음 확인
by_id: carol → alice → dave → bob
by_name: alice → bob → carol → dave

1. display d->by_name[0] → 0x5555555592e0, 곧 free할 주소 가리킴
2. tcachebins → 해당 주소가 이미 tcache 리스트 안에 있음
3. x/4gx 0x5555555592e0 → 원래 Rec의 데이터가 있던 자리에 free() 이후 tcache에서 사용하는 값이 남아 있는 것을 확인
참고로 tcache는 한 bin에 최대 7개의 청크가 들어가기 때문에, 8번째로 해제된 Rec인 bob(0x555555559360)은 tcache가 아닌 fastbin으로 넘어간다. 그래서 tcachebins에는 7개만 보인다.
static void directory_add(Directory *d, int id, const char *name) {
Rec *r = rec_new(id, name);
d->by_id[d->count] = r;
d->by_name[d->count] = r; /* 같은 포인터를 두 인덱스에 함께 등록 */
d->count++;
}
add에서는 Rec *r 하나를 만든 다음, 그 포인터를 by_id와 by_name에 둘 다 저장
→이것만으론 owner를 못 정함
판단근거: directory_free의 첫 번째 루프
free(d->by_id[i]->name);
free(d->by_id[i]);
name 문자열까지 free하는 쪽이 by_id그래서 by_id가 owner, by_name은 free하면 안 됨. 루프 2는 이 사실을 까먹고 추가된 코드로 보임
by_name을 free()하는 두 번째 루프를 삭제
static void directory_free(Directory *d) {
for (int i = 0; i < d->count; i++) {
free(d->by_id[i]->name);
free(d->by_id[i]);
}
/* --- */
d->count = 0;
}