转换函数指针

Ben*_*ler 15 c function-pointers qsort

我正在编写一个函数,它接收一个指向比较函数的指针和一个数组,MyStructs并且应该根据比较函数对数组进行排序:

void myStructSort(
                  struct MyStruct *arr,
                  int size,
                  int (*comp)(const struct MyStruct *, const struct MyStruct *)) {
  qsort(arr, size, sizeof(struct MyStruct), comp);
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,这不会编译,因为qsort期望比较器接收void *参数而不是const struct MyStruct *.我想到了几个不好的解决方案,并想知道正确的解决方案是什么.

选项1

铸造compint (*)(const void *, const void*).这编译但是未定义的行为(参见这个问题).

选项2

创建一个全局变量int (*global_comp)(const struct MyStruct *, const struct MyStruct *),并设置global_comp=comp里面myStructSort.然后创建一个函数:

int delegatingComp(const void *a, const void *b) {
  return globalComp((const struct MyStruct *)a, (const struct MyStruct *)b);
}
Run Code Online (Sandbox Code Playgroud)

而在myStructSort通话qsort(arr, size, sizeof(struct MyStruct), delegatingComp).这个问题是icky全局变量.

选项3

重新实现qsort.这是功能安全但非常糟糕的做法.

有没有神奇的完美第四选择?

编辑

我无法更改API,myStructSort我正在编译我的代码使用gcc c99 -Wall -Wextra -Wvla.

Kla*_*äck 10

选项2打破了线程安全性,所以我不会选择那个.

你指出,选项3是完全错误的.没有理由重新实施快速排序并可能犯错误.

选项1是UB,但它适用于任何理智的编译器.如果选择此选项,请务必添加注释.

我还会考虑:

选项4.重新设计完全myStructSort接受int (*)(const void *, const void*)或废弃的界面并qsort直接致电.基本上把它发回给architecht,因为他做了一个糟糕的设计选择.

  • 对于选项2,您可以将"全局"放在线程本地存储中. (4认同)

Jas*_* Hu 5

以下方法仅适用于gcc.它是gnu扩展的一部分.进一步请参考https://gcc.gnu.org/onlinedocs/gcc-4.8.5/gcc/Nested-Functions.html#Nested-Functions

首先让我们确保原型qsort这样的形式:

void qsort(void *base, size_t nmemb, size_t size,
           int (*compar)(const void *, const void *));
Run Code Online (Sandbox Code Playgroud)

然后你可以:

void myStructSort(
                  struct MyStruct *arr,
                  int size,
                  int (*comp)(const struct MyStruct *, const struct MyStruct *)) {
  int comparator(const void * a, const void *b) {
    return comp((const struct MyStruct *)a, (const struct MyStruct *)b);
  }
  qsort(arr, size, sizeof *arr, comparator);
}
Run Code Online (Sandbox Code Playgroud)

但是,再次,因为它使用gnu扩展,不要指望太多的可移植性.

关于你的评论:对于现代gcc,gnu标准是默认标准而不是iso标准.具体而言,最新gcc应该使用gnu11标准.老年人正在使用gnu89.所以,我不知道你的命令行参数,但如果-std没有设置,这将工作.

以下是一个例子info gcc,以防链接死亡.它显示了嵌套函数的类似闭包的用法:

 bar (int *array, int offset, int size)
 {
   int access (int *array, int index)
     { return array[index + offset]; }
   int i;
   /* ... */
   for (i = 0; i < size; i++)
     /* ... */ access (array, i) /* ... */
 }
Run Code Online (Sandbox Code Playgroud)