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
90: Searching with bsearch
You might be tempted to think of bsearch as a general-purpose "find" tool—a more efficient version of a for loop that scans an array for a specific value. I've seen many developers drop bsearch into a project thinking it's just a performance upgrade over a linear search. This is a dangerous assumption.
Thinking bsearch works on any array
Here is the reality: if you pass an unsorted array to bsearch, the function will not "sort it for you," nor will it reliably find your data. It will simply fail, often returning NULL even when the item is sitting right there in the array. Let me show you why.
#include
#include
#include
int compare_ints(const void *a, const void *b) {
return (*(int*)a - *(int*)b);
}
int main() {
// Unsorted array
int numbers[] = {10, 5, 20, 2, 15};
int key = 5;
int *item = bsearch(&key, numbers, 5, sizeof(int), compare_ints);
if (item == NULL) {
printf("Not found!\n"); // This will likely print, even though 5 is in the list.
}
return 0;
}
Binary search works by jumping to the middle of the range and deciding whether to go left or right. If the array isn't sorted, that decision is based on a lie. The algorithm might look at the middle element, see that 5 is smaller than 20, and discard the entire right half of the array—which might be exactly where your target was hiding. It's fast precisely because it ignores most of the data; if that data isn't ordered, the logic collapses.
The strict requirement for pre-sorted data
To use bsearch, your data must be sorted according to the same comparison logic you pass into the function. In professional C code, bsearch is almost always paired with qsort. If the data is static, you sort it once at startup. If it's dynamic, you have to weigh the cost of sorting against the speed of the search.
I usually tell people to think of it as a contract: bsearch promises O(log n) speed, but in exchange, you promise that the array is already ordered. If you break that contract, the behavior is undefined in practice (though usually it just returns NULL).
Handling the generic void pointers
One of the clunkiest parts of bsearch is the void * signatures. Because the function is designed to work with any data type—integers, strings, or complex structs—it doesn't know what it's actually comparing. You have to do the casting yourself inside the comparison function.
Let's look at a real-world example. Suppose we have a list of Employee records and we want to find one by their ID number.
typedef struct {
int id;
char name[50];
} Employee;
int compare_employees(const void *a, const void *b) {
const Employee *empA = (const Employee *)a;
const Employee *empB = (const Employee *)b;
// Return negative if A < B, zero if A == B, positive if A > B
return (empA->id - empB->id);
}
// ... inside main ...
Employee staff[] = { {101, "Alice"}, {105, "Bob"}, {110, "Charlie"} };
Employee key = {105, ""}; // We only care about the ID for the search
Employee *found = bsearch(&key, staff, 3, sizeof(Employee), compare_employees);
if (found) {
printf("Found employee: %s\n", found->name);
}
Notice a few critical details here. First, the key is a full Employee struct. bsearch expects the key to be of the same type as the elements in the array. Second, the comparison function casts those void * pointers back to Employee * before accessing the id field. This is the "C way"—it's not as pretty as generics in other languages, but it's incredibly powerful once you stop fighting the syntax.
📋 Practical Task
Implementing a High-Speed Product SKU Lookup
You are building a warehouse management system. You have an array of Product structs, each containing a sku (an integer) and a price (a float). The products are currently provided to you in a random order.
Your task:
- Define a
Productstruct. - Create an unsorted array of at least 5 products.
- Write a comparison function compatible with both
qsortandbsearch. - Sort the array using
qsort. - Implement a search that takes a SKU number from the user (or a hardcoded variable) and uses
bsearchto find the corresponding product. - Print the price of the product if found, or a "Product not found" message if it isn't.
There are no comments for now.