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 array
  • nmemb — number of elements
  • size — size of each element in bytes
  • compar — 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        78

Multi-key sorting: compare the primary key first. If equal, compare secondary key. This pattern extends to any number of keys.

The Comparator Contract

Return valueMeaning
Negativea should come before b
0a and b are equal (order unspecified)
Positivea 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 - b for 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 qsort and bsearch on the same array
  • Descending sort: swap arguments in the comparator or call the ascending comparator with reversed args