Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I use memcmp along with qsort?

I am making C dynamic array library, kind of. Note that I'm doing it for fun in my free time, so please do not recommend million of existing libraries.

I started implementing sorting. The array is of arbitrary element size, defined as struct:

typedef struct {
  //[PRIVATE] Pointer to array data
  void *array;
  //[READONLY] How many elements are in array
  size_t length;
  //[PRIVATE] How many elements can further fit in array (allocated memory)
  size_t size;
  //[PRIVATE] Bytes per element
  size_t elm_size;
} Array;

I originally prepared this to start with the sort function:

/** sorts the array using provided comparator method
 * if metod not provided, memcmp is used
 * Comparator signature
 *  int my_comparator ( const void * ptr1, const void * ptr2, size_t type_size );
**/
void array_sort(Array* a, int(*comparator)(const void*, const void*, size_t)) {
    if(comparator == NULL)
        comparator = &memcmp;
    // Sorting algorithm should follow
}

However I learned about qsort:

void qsort (void* base, size_t num, size_t size, int (*compar)(const void*,const void*));

Apparently, I could just pass my internal array to qsort. I could just call that:

qsort (a->array, a->length, a->elm_size, comparator_callback);

But there's a catch - qsort's comparator signature reads as:

int (*compar)(const void*,const void*)

While memcmp's signature is:

int memcmp ( const void * ptr1, const void * ptr2, size_t type_size );

The element size is missing in qsort's callback, meaning I can no longer have a generic comparator function when NULL is passed as callback. I could manually generate comparators up to X bytes of element size, but that sounds ugly.

Can I use qsort (or other sorting built-in) along with memcpy? Or do I have to chose between built-in comparator and built-in sorting function?

like image 269
Tomáš Zato - Reinstate Monica Avatar asked Nov 28 '16 23:11

Tomáš Zato - Reinstate Monica


Video Answer


1 Answers

C11 provides you with an (admittedly optional) qsort_s function, which is intended to deal with this specific situation. It allows you to pass-through a user-provided void * value - a context pointer - from the calling code to the comparator function. The comparator callback in this case has the following signature

int (*compar)(const void *x, const void *y, void *context)

In the simplest case you can pass a pointer to the size value as context

#define __STDC_WANT_LIB_EXT1__ 1
#include <stdlib.h>
...

int comparator_callback(const void *x, const void *y, void *context)
{
  size_t elm_size = *(const size_t *) context;
  return memcmp(x, y, elm_size);
}

...
qsort_s(a->array, a->length, a->elm_size, comparator_callback, &a->elm_size);

Or it might make sense to pass a pointer to your entire array object as context.

Some *nix-based implementations have been providing a similar qsort_r function for a while, although it is non-standard.

like image 77
AnT Avatar answered Sep 21 '22 13:09

AnT