Names specified here
Name Description Notes Source Availability
bsearch() Search array (·) <stdlib.h> C89 C90 C95 C99 C11
bsearch_s() Search array ? (·) <stdlib.h> C11
qsort() Sort array (·) <stdlib.h> C89 C90 C95 C99 C11
qsort_s() Sort array ? (·) <stdlib.h> C11

The standard library supports the sorting of arrays on any criteria, and searching an array sorted on any criteria. As an example, we will define a structure to represent a person, and an array of such structures with the data already filled in:

struct person {
  char gn[30]; // given name
  char sn[30]; // surname
  time_t dob; // date of birth
  long long credit;
};

struct person database[] = {
  ...
};
#define ENTRY_COUNT (sizeof database / sizeof database[0])

We can define an ordering for these structures, primarily sorting on surname, then given name if surnames are identical, then date of birth if given names are also identical:

int compare_persons(const struct person *p1,
                    const struct person *p2)
{
  {
    int rc = strcmp(p1->sn, p2->sn);
    if (rc != 0) return rc;
  }

  {
    int rc = strcmp(p1->gn, p2->gn);
    if (rc != 0) return rc;
  }

  return copysign(1.0, difftime(p1->dob, p2->dob));
}

Note that we specify it to return negative if *p1 should be ordered earlier than *p2, positive if later, and zero if there is no ordering. Note also that we are not interested in ordering according to credit.

Sorting arrays

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_LIB_EXT1__ 1
#include <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_MAX.

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.


CHaR
Sitemap Supported
Site format updated 2024-06-05T22:37:07.391+0000
Data updated 1970-01-01T00:00:00.000+0000
Page updated 2022-06-17T21:43:05.000+0000