我可以和memcmp一起使用qsort吗?

时间:2016-11-28 23:36:01

标签: c arrays memcpy qsort

我正在制作C动态数组库,有点像。请注意我在空闲时间这样做是为了好玩,所以请不要推荐数百万现有的库。

我开始实施排序。该数组具有任意元素大小,定义为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;

我最初准备这个以sort函数开始:

/** 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
}

但是我了解了qsort

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

显然,我可以将内部数组传递给qsort。我可以这么说:

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

但是有一个问题 - qsort的比较器签名读作:

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

虽然memcmp的签名是:

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

qsort的回调中缺少元素大小,这意味着当NULL作为回调传递时,我不再具有通用比较器函数。我可以手动生成最多X字节元素大小的比较器,但听起来很难看。

我可以使用qsort(或其他内置排序)和memcpy吗?或者我必须在内置比较器和内置排序功能之间进行选择?

3 个答案:

答案 0 :(得分:4)

C11为您提供(诚然可选)qsort_s function,旨在处理这种特定情况。它允许您传递用户提供的country值 - 一个上下文指针 - 从调用代码到比较器函数。在这种情况下,比较器回调具有以下签名

SELECT
    id1,
    id2,
    MAX(country),
    SUM(value)
FROM
    your_table
GROUP BY
    id1,
    id2

在最简单的情况下,您可以将指针传递给大小值作为上下文

void *

或者将指针作为上下文传递给整个数组对象可能是有意义的。

一些基于* nix的实现已经提供了类似的qsort_r function一段时间,虽然它是非标准的。

答案 1 :(得分:1)

非线程安全的方法是使用私有全局变量来传递大小。

static size_t compareSize = 0;

int defaultComparator(const void *p1, const void *p2) {
  return memcmp(p1, p2, compareSize);
}

void array_sort(Array* a, int(*comparator)(const void*, const void*, size_t)) {
    if(comparator == NULL) {
      compareSize = a->elm_size;
      comparator = &defaultComparator;
    }
    // Sorting algorithm should follow
}

您可以通过make compareSize线程局部变量(__thread

使其成为线程安全的

答案 2 :(得分:1)

qsort() API是简单时代的遗产。应该有一个额外的“环境”指针从qsort()调用中不加改变地传递给每个比较。这将允许您以线程安全的方式传递对象大小和任何其他必要的上下文。

但它不存在。 @ BryanChen的方法是唯一合理的方法。

我写这个答案的主要原因是指出memcmp做一些有用的事情很少。没有多种对象可以通过成分unsigned char的词典顺序进行比较。

当然比较struct这种方式是危险的,因为未指定填充字节值。即使比较的平等部分也会失败。换句话说,

struct foo { int i; };

void bar(void) { 
  struct foo a, b;
  a.i = b.i = 0;
  if (memcmp(&a, &b, sizeof a) == 0) printf("equal!");
}

可能 - 按照C标准 - 什么都不打印!

另一个例子:对于像unsigned int这样简单的事情,你会得到大端与小端存储顺序的不同排序顺序。

unsigned a = 0x0102;
unsigned b = 0x0201;
printf("%s", memcmp(&a, &b, sizeof a) < 0 ? "Less! : "More!");

将打印LessMore,具体取决于正在运行的计算机。

实际上,我能想象的唯一对象类型与memcmp进行比较是有意义的无符号字节块。这不是一个非常常见的排序用例。

总之,提供memcmp作为比较功能的库注定容易出错。有人会尝试用它来代替专门的比较,这是获得理想结果的唯一途径。