qsort와 bsearch는 함수 이름보다 비교 함수 계약을 정확히 지키는 것이 더 중요합니다.
qsort비교 함수에서 뭘 반환해야 하는지 다시 헷갈린다return a - b;가 왜 위험한지 기억이 안 난다bsearch가 왜 아무것도 못 찾는지 확인하고 싶다
숏컷 코드
비교 함수는 "앞에 와야 하면 음수, 같으면 0, 뒤에 와야 하면 양수"를 돌려줘야 합니다.
int cmp_int(const void *a, const void *b) {
int ia = *(const int *)a;
int ib = *(const int *)b;
return (ia > ib) - (ia < ib);
}이 한 줄이 중요한 이유는 두 가지입니다.
- 반환 규약을 정확히 지킨다
ia - ib처럼 정수 오버플로우 위험이 없다
문법
int arr[] = {5, 2, 8, 1, 9};
qsort(arr, 5, sizeof(arr[0]), cmp_int);각 인자의 의미는 이렇습니다.
- 배열 시작 주소
- 원소 개수
- 원소 하나의 크기
- 비교 함수
실전에서 자주 깨지는 부분은 sizeof(arr) 전체 바이트 수를 세 번째 인자로 넣는 실수입니다. 세 번째는 원소 하나의 크기입니다.
bsearch는 배열이 이미 같은 기준으로 정렬되어 있어야 합니다.
int key = 8;
int *found = bsearch(&key, arr, 5, sizeof(arr[0]), cmp_int);이 코드는 배열이 cmp_int 기준으로 이미 정렬돼 있을 때만 맞습니다.
qsort로 오름차순 정렬- 같은 비교 함수로
bsearch
정렬 기준과 검색 기준이 다르면 찾지 못해도 이상한 일이 아닙니다.
비교 함수
typedef struct {
char name[32];
int score;
} Student;
int cmp_score_desc(const void *a, const void *b) {
const Student *sa = (const Student *)a;
const Student *sb = (const Student *)b;
return (sb->score > sa->score) - (sb->score < sa->score);
}구조체에서 중요한 것은 "무엇을 기준으로 정렬하느냐"를 카드 하나에 명확히 적는 것입니다.
- 점수 정렬
- 이름 정렬
- 날짜 정렬
기준이 달라지면 비교 함수도 달라집니다.
체크포인트
return ia - ib;로 비교해서 오버플로우bsearch전에 정렬하지 않음qsort와bsearch가 서로 다른 비교 함수를 씀- 원소 크기에
sizeof(arr)를 넣음 - 문자열 정렬에서
strcmp대신 주소 비교를 해버림
주의할 점
bsearch는 실패하면 NULL을 돌려주지만, 정렬되지 않은 배열에서는 "실패"가 아니라 "전제가 깨진 상태"입니다. 찾지 못했다고 바로 key가 없다고 단정하지 말고, 먼저 정렬 여부와 비교 함수 일치 여부를 확인하세요.
참고 링크
2 sources