C’s standard library provides qsort for sorting any array of any type. It uses function pointers to make the comparison generic. Most programmers get the comparator wrong the first time — the return value rules are easy to confuse with strcmp, which has the same convention but for a different reason.
The qsort Signature
#include <stdlib.h>
void qsort(void *base, size_t nmemb, size_t size,
int (*compar)(const void *, const void *));base— pointer to the first element of the arraynmemb— number of elementssize— size of each element in bytescompar— comparison function: returns negative, zero, or positive
Sorting Integers
#include <stdio.h>
#include <stdlib.h>
int compare_int_asc(const void *a, const void *b) {
int ia = *(const int*)a;
int ib = *(const int*)b;
/* WRONG: return ia - ib; — overflow for large negatives */
if (ia < ib) return -1;
if (ia > ib) return 1;
return 0;
}
int compare_int_desc(const void *a, const void *b) {
return compare_int_asc(b, a); /* reverse arguments for descending */
}
int main() {
int arr[] = {5, 2, 8, 1, 9, 3, 7, 4, 6};
size_t n = sizeof(arr) / sizeof(arr[0]);
qsort(arr, n, sizeof(int), compare_int_asc);
for (size_t i = 0; i < n; i++) printf("%d ", arr[i]);
printf("n"); /* 1 2 3 4 5 6 7 8 9 */
qsort(arr, n, sizeof(int), compare_int_desc);
for (size_t i = 0; i < n; i++) printf("%d ", arr[i]);
printf("n"); /* 9 8 7 6 5 4 3 2 1 */
return 0;
}The subtraction trick (return a - b) is tempting but wrong when values span negative and positive ranges — INT_MIN - 1 overflows. Always use the three-branch comparison for integers.
Sorting Strings
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int compare_str(const void *a, const void *b) {
/* a and b are pointers to char* (elements of a char* array) */
const char *sa = *(const char **)a;
const char *sb = *(const char **)b;
return strcmp(sa, sb);
}
int main() {
const char *words[] = {"banana", "apple", "cherry", "date", "avocado"};
size_t n = sizeof(words) / sizeof(words[0]);
qsort(words, n, sizeof(char*), compare_str);
for (size_t i = 0; i < n; i++) printf("%sn", words[i]);
/* apple, avocado, banana, cherry, date */
return 0;
}The double indirection (*(const char **)a) is the part that trips people up. The array elements are char* pointers. qsort passes pointers to those elements — so a is a char**, and you dereference it to get the char* string.
Sorting Structs
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char name[32];
int score;
int age;
} Student;
int by_score_desc(const void *a, const void *b) {
const Student *sa = (const Student *)a;
const Student *sb = (const Student *)b;
if (sa->score > sb->score) return -1;
if (sa->score < sb->score) return 1;
return strcmp(sa->name, sb->name); /* tie-break by name */
}
int main() {
Student students[] = {
{"Alice", 95, 20},
{"Bob", 87, 22},
{"Carol", 95, 21},
{"Dave", 78, 19},
};
size_t n = sizeof(students) / sizeof(students[0]);
qsort(students, n, sizeof(Student), by_score_desc);
for (size_t i = 0; i < n; i++) {
printf("%-10s %3dn", students[i].name, students[i].score);
}
return 0;
}Alice 95
Carol 95
Bob 87
Dave 78Multi-key sorting: compare the primary key first. If equal, compare secondary key. This pattern extends to any number of keys.
The Comparator Contract
| Return value | Meaning |
|---|---|
| Negative | a should come before b |
| 0 | a and b are equal (order unspecified) |
| Positive | a should come after b |
The comparator must be a strict weak ordering: irreflexive (cmp(a,a) == 0), asymmetric (if cmp(a,b) < 0 then cmp(b,a) > 0), and transitive. A comparator that violates these rules produces undefined behavior — qsort may not terminate or may corrupt memory.
bsearch — Binary Search Using the Same Comparator
#include <stdio.h>
#include <stdlib.h>
int compare_int_asc(const void *a, const void *b) {
int ia = *(const int*)a;
int ib = *(const int*)b;
if (ia < ib) return -1;
if (ia > ib) return 1;
return 0;
}
int main() {
int arr[] = {1, 3, 5, 7, 9, 11, 13}; /* must be sorted */
size_t n = sizeof(arr) / sizeof(arr[0]);
int key = 7;
int *result = (int*)bsearch(&key, arr, n, sizeof(int), compare_int_asc);
if (result) printf("Found %d at index %tdn", key, result - arr);
else printf("Not foundn");
return 0;
}Reuse the same comparator for qsort and bsearch. The array must be sorted in the same order the comparator produces. Use the same function pointer for both. Test sorting with small arrays in our c compiler first — print before and after to verify your comparator is correct.
TL;DR
- qsort takes a comparator: negative = a before b, 0 = equal, positive = a after b
- Never use
return a - bfor integers — it overflows for extreme values - For arrays of pointers (like
char**), dereference to get the actual value:*(const char **)a - Multi-key sort: compare primary key first, use secondary key only when primary is equal
- The comparator must be a strict weak ordering — violating this causes undefined behavior
- Use the same comparator for
qsortandbsearchon the same array - Descending sort: swap arguments in the comparator or call the ascending comparator with reversed args