C
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions
-
Section 4: Arrays and Strings
-
Section 5: Pointers
-
Section 6: Memory Management
-
Section 7: Structures and Unions
-
Section 8: The Preprocessor and Build Process
-
Section 9: Standard Library: stdio.h
-
Section 10: Standard Library: stdlib.h
-
Section 11: Standard Library: string.h
-
Section 12: Standard Library: ctype.h and wctype.h
-
Section 13: Standard Library: math.h, complex.h, fenv.h, tgmath.h
-
Section 14: Standard Library: Type and Limit Headers
-
Section 15: Standard Library: Error Handling and Debugging
-
Section 16: Standard Library: Localization and Encoding
-
Section 17: Standard Library: time.h
-
Section 18: Standard Library: Concurrency (C11)
-
Section 19: POSIX and System Programming (unistd.h)
-
Section 20: More Data Structures
-
Section 21: Algorithms in C
-
Section 22: Bitwise Operations
-
Section 23: Command-Line Programs
-
Section 24: Debugging and Best Practices
-
Section 25: Compiler and Language Internals
-
Section 26: Embedded and Cross-Platform Considerations
-
Section 27: Networking Basics
-
Section 28: Practical Projects
-
Section 29: Interview Practice
-
Section 30: C23 Modern Features
-
Section 31: More Practice and Review
89: Sorting with qsort
A few years ago, I worked with a junior developer who spent an entire weekend implementing a custom QuickSort from scratch. He was building a small game engine and needed to sort a list of enemy NPCs by their distance to the player to handle rendering priority. He had a subtle bug in his partition logic that only triggered when exactly three enemies were at the same distance, causing a segmentation fault that drove him crazy. When I finally looked at his code, I asked why he was reinventing the wheel. He didn't realize that the C standard library already provides a highly optimized, battle-tested sorting function. He had spent forty-eight hours debugging something that could have been solved with a single function call to qsort.
The Generic Nature of qsort
The power of qsort, found in <stdlib.h>, is that it is generic. It doesn't know if you're sorting integers, floating-point numbers, or a complex custom struct. Because it doesn't know the data type, it can't possibly know how to determine if one element is "less than" another. This is where you come in. You provide qsort with a comparison function—a pointer to a function you've written that tells qsort how to compare two elements of your specific type.
The function signature for qsort looks like this: void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));. The first three arguments are straightforward: the pointer to the start of the array, the number of elements, and the size of each element in bytes. The fourth argument is the tricky part. It's a function pointer to your comparison logic.
Writing the Comparison Logic
Your comparison function must take two const void * arguments and return an integer. This is the "contract" with qsort: if the return value is negative, the first element comes before the second; if positive, the second comes first; if zero, their relative order doesn't matter. Since the arguments are void *, you have to cast them back to your actual data type before you can dereference them.
Here is how I usually handle a simple array of integers:
int compare_ints(const void *a, const void *b) {
int arg1 = *(const int *)a;
int arg2 = *(const int *)b;
if (arg1 < arg2) return -1;
if (arg1 > arg2) return 1;
return 0;
}
You might be tempted to just return arg1 - arg2. While that works for small positive integers, it's a dangerous habit. If you're dealing with very large positive and negative numbers, the subtraction can overflow, leading to an incorrect sort or a crash. Explicit comparisons are safer and clearer.
Sorting Complex Structs
The real utility of qsort appears when you're dealing with structures. Let's say you have a Player struct and you want to sort a leaderboard by score in descending order. You don't need to move the actual structs around manually; you just tell qsort how to look inside the struct to find the score.
typedef struct {
char name[32];
int score;
} Player;
int compare_players(const void *a, const void *b) {
const Player *playerA = (const Player *)a;
const Player *playerB = (const Player *)b;
// For descending order, we flip the logic:
// return -1 if B is smaller than A
if (playerA->score > playerB->score) return -1;
if (playerA->score < playerB->score) return 1;
return 0;
}
// Usage:
// qsort(leaderboard, num_players, sizeof(Player), compare_players);
I've seen developers struggle here because they forget that qsort passes pointers to the elements, not the elements themselves. Always remember: cast the void * to your type pointer, then dereference it to get to your data.
📋 Practical Task
Implement a Product Price Sorter
You are building a simple inventory system. You have a Product struct containing a name (string) and a price (double). Your task is to implement a comparison function and use qsort to sort an array of products from cheapest to most expensive.
Requirements:
- Define a
Productstruct withchar name[50]anddouble price. - Write a comparison function
compare_productsthat handlesdoublevalues correctly (remember that you cannot simply subtract doubles and return anintwithout risking precision loss or incorrect results). - Create an array of at least 5 products with unsorted prices.
- Call
qsortand print the resulting sorted list to the console.
There are no comments for now.