We can sort an array like the one above using either of these functions:
#include <stdlib.h>
void qsort(void *base,
size_t nmemb,
size_t size,
int (*cmp)(const void *elem1,
const void *elem2));
#define __STDC_WANT_ 1 #includeLIB_ EXT1__ <stdlib.h>
errno_t qsort_s(void *base, rsize_t nmemb, rsize_t size, int (*cmp)(const void *elem1, const void *elem2, void *ctxt), void *ctxt);
Both take the start of an array base
, the number of elements in the array
nmemb
, the size of each element in
the array size
, and a pointer to a
comparison function cmp
. Like our
compare_persons
function, the
comparison function is expected to take two pointers
elem1
and elem2
, and yield positive, negative or zero
according to their relative ordering. Because of the
differing parameter types, we cannot pass a pointer to
compare_persons
directly to these
functions without rewriting it. An alternative is to
write a wrapper with the correct types:
static int cmpper_for_qsort(const void *elem1, const void *elem2) { return compare_persons(elem1, elem2); } qsort(database, ENTRY_COUNT, sizeof database[0], &cmpper_for_qsort);
The wrapper for qsort_s
is similar:
static int cmpper_for_qsort_s(const void *elem1, const void *elem2, void *ctxt) { return compare_persons(elem1, elem2); } qsort_s(database, ENTRY_COUNT, sizeof database[0], &cmpper_for_qsort_s, NULL);
The comparison function will only be invoked with
elem1
and elem2
pointing to elements of the specified
array. The final argument to qsort_s
is
passed unmodified as the third argument ctxt
in every call to the comparison function,
allowing user-defined context to be passed through
without using static storage. In summary, we inject the
behaviour defined by our comparison function into the
sorting algorithm that qsort
and
qsort_s
implement. Despite the name, there is no reason to
believe that the algorithm is Quick
Sort; the function may use any algorithm, and may
even choose different algorithms based on, for example,
the size of the array, the size of elements, etc.
qsort_s
returns non-zero if nmemb
or
size
exceed RSIZE_
.
The sorting functions re-arrange elements by copying them byte-by-byte, which is possible because the size of each element is provided. To reduce copying, it might be appropriate to have a separate array of pointers to the structures, and sort the pointers:
const struct person *ptrs[ENTRY_COUNT] = { &database[0], &database[1], &database[2], ... }; static int cmpper_for_qsort(const void *elem1, const void *elem2) { const struct person *const *pp1 = elem1; const struct person *const *pp2 = elem2; return compare_persons(*pp1, *pp2); } qsort(ptrs, ENTRY_COUNT, sizeof ptrs[0], &cmpper_for_qsort);
This technique is also appropriate if you want to build multiple indices to the same array, or an index to something that isn't an array.