简体   繁体   English

使用void指针和type参数对C中的多个类型进行排序的函数

[英]sorting function for multiple types in C using void pointer and type argument

In this problem, A function that takes a void pointer to an array, number of elements and an integer indicating type of elements should sort the array. 在此问题中,一个将void指针指向数组,元素数量和一个指示元素类型的整数的函数应该对该数组进行排序。 Are there any tricks to avoid writing same code 4 times as in the following solution? 有什么技巧可以避免像下面的解决方案一样重复编写4次相同的代码?

//type 1:short, 2:int, 3:float, 4:double
void Sort(void *values, int nValues, int type) {
    int i, j, temp;

    switch(type) {
    case 1: //short
    {
        short *ptr = (short *)values;
        for (i = 0; i < nValues - 1; i++)
            for (j = i; j < nValues; j++)
                if (ptr[i] > ptr[j]) {
                    temp = ptr[i];
                    ptr[i] = ptr[j];
                    ptr[j] = temp;
                }
        break;
    }
    case 2: // int
    {
        int *ptr = (int *)values;
        for (i = 0; i < nValues - 1; i++)
            for (j = i; j < nValues; j++)
                if (ptr[i] > ptr[j]) {
                    temp = ptr[i];
                    ptr[i] = ptr[j];
                    ptr[j] = temp;
                }
        break;
    }
    case 3: // float
    {
        float *ptr = (float *)values;
        for (i = 0; i < nValues - 1; i++)
            for (j = i; j < nValues; j++)
                if (ptr[i] > ptr[j]) {
                    temp = ptr[i];
                    ptr[i] = ptr[j];
                    ptr[j] = temp;
                }
        break;
    }
    case 4: // double
    {
        double *ptr = (double *)values;
        for (i = 0; i < nValues - 1; i++)
            for (j = i; j < nValues; j++)
                if (ptr[i] > ptr[j]) {
                    temp = ptr[i];
                    ptr[i] = ptr[j];
                    ptr[j] = temp;
                }
    }
    }
}

Yes, there is an alternative. 是的,有替代方法。 In the C standard library there already is a function called qsort (which probably is an abbreviation of quick sort, but it could be any other sorting algorithm as well). C标准库中,已经有一个名为qsort 的函数 (它可能是快速排序的缩写,但也可以是任何其他排序算法)。 To use that function, you pass it the array, the number of elements in the array, the size of each element and a comparison function. 要使用该函数,请向其传递数组,数组中元素的数量,每个元素的大小以及比较函数。

You just need to write the comparison function for each data type, as shown in the example on that page. 您只需要为每种数据类型编写比较函数,如该页面上的示例所示。

If you want to merge the 4 comparison functions into a single function, you have to pass some context information to the comparison function. 如果要将4个比较功能合并为一个功能,则必须将一些上下文信息传递给比较功能。 In that case you cannot use qsort anymore but have to use qsort_s . 在那种情况下,您不能再使用qsort ,而必须使用qsort_s Then your comparison function could look like: 然后,您的比较函数可能如下所示:

#define compare(a, b) (((a) > (b)) - ((b) > (a)))
#define compare_ptr(type) (compare(*(type *)(p1), *(type *)(p2)))

static int compare_by_type(const void *p1, const void *p2, void *ctx) {
    int type = *(int *) ctx;
    switch (type) {
    case 1: return compare_ptr(short);
    case 2: return compare_ptr(int);
    case 3: return compare_ptr(float);
    case 4: return compare_ptr(double);
    default: return 0;
    }
}

#undef compare
#undef compare_ptr

int main(void) {
    int iarray[] = {1, 6, 4, 9, 55, 999, -33333};
    int sort_type = 1;

    qsort_s(iarray, 7, sizeof(int), compare_by_type, &type);
}

That's some fairly advanced stuff: 那是一些相当高级的东西:

  • passing function pointers around 传递函数指针
  • doing pointer stuff with pointers to arbitrary types 用指向任意类型的指针做指针
  • using macros that accept type names as macro parameter 使用接受类型名称作为宏参数的宏
  • mixing boolean arithmetic with integer arithmetic 将布尔运算与整数运算混合

But in the end, it's trivial to add more types to the list, as long as they support the < operator. 但是最后,只要它们支持<运算符,就可以在列表中添加更多类型。

Note that float and double don't even belong to this category since their operator < returns false as soon as one of the numbers is NaN , which means Not A Number, and results from expressions such as 0.0 / 0.0 . 请注意, floatdouble甚至都不属于此类别,因为它们的运算符<一旦数字之一为NaN时即返回false ,即表示非A数值,并且是由诸如0.0 / 0.0表达式得出的。 As soon as you have such a value in the array, the behavior becomes undefined. 一旦在数组中具有这样的值,该行为就变得不确定。 The sorting function might even get stuck in an endless loop. 排序功能甚至可能陷入无限循环。 To fix this, change the definition of the compare macro: 要解决此问题,请更改compare宏的定义:

#define compare(a, b) (((a) > (b)) - !((b) <= (a)))

It looks even more complicated now, but works for NaN . 现在看起来更加复杂,但适用于NaN For example: 例如:

compare(NaN, 5)
= (NaN > 5) - !(5 <= NaN)
= false - !(5 <= NaN)
= false - !(false)
= false - true
= 0 - 1
= -1

This means that NaN will be sorted to the front of the array. 这意味着NaN将被排序到数组的前面。

compare(NaN, NaN)
= (NaN > NaN) - !(NaN <= NaN)
= false - true
= -1

Dammit. 该死 Comparing two NaNs should have resulted in 0, meaning they are equal. 比较两个NaN应该得出0,表示它们相等。 So in that special case there needs to be a correction: 因此,在这种特殊情况下,需要进行更正:

#define compare(a, b) (((a) > (b)) - ((b) > (a)) - ((a) != (a) || (b) != (b)))

Since NaN is the only value that compares unequal to itself, this additional code does not affect the integer arithmetic and should be optimized away by the compiler. 由于NaN是唯一一个不等于其自身的值,因此此附加代码不会影响整数算术,应由编译器对其进行优化。

Here are two different approached to avoiding code duplication for your problem: 以下是两种避免代码重复的方法:

If you cannot use library functions or want to keep your algorithm, here is a solution with a preprocessor macro: 如果您不能使用库函数或想保留算法,则可以使用预处理器宏解决方案:

#define SORT_TYPE(values, nValues, type) do {\
        type *ptr = (type *)(values);        \
        int i, j, n = (nValues);             \
        for (i = 0; i < n - 1; i++) {        \
            for (j = i + 1; j < n; j++) {    \
                if (ptr[i] > ptr[j]) {       \
                    type temp = ptr[i];      \
                    ptr[i] = ptr[j];         \
                    ptr[j] = temp;           \
                }                            \
            }                                \
        }                                    \
    } while (0)

//type 1:short, 2:int, 3:float, 4:double
void Sort(void *values, int nValues, int type) {
    switch (type) {
      case 1: //short
        SORT_TYPE(values, nValues, short);
        break;
      case 2: // int
        SORT_TYPE(values, nValues, int);
        break;
      case 3: // float
        SORT_TYPE(values, nValues, float);
        break;
      case 4: // double
        SORT_TYPE(values, nValues, double);
        break;
    }
}

Notes: 笔记:

  • The macro SORT_TYPE can be invoked with arguments that have side effects as they are evaluated once only, but it is still fragile: the code generated would break if the arguments were expressed in terms of variables names ptr , i , j or n . SORT_TYPE可以使用仅具有一次评估的具有副作用的参数来调用,但是它仍然很脆弱:如果以变量名ptrijn表示参数,则生成的代码将中断。 It is possible to try and make these collisions less likely by naming the block variables ptr__ , or some other contorted way, but it does not completely solve the problem. 可以通过命名块变量ptr__或其他某种扭曲的方式来尝试减少冲突,但是并不能完全解决问题。 Macros must be written with extreme attention and used with care. 编写宏时必须格外注意并小心使用。

  • NaN values in float and double arrays may not be handled properly as the comparisons will return false if one or both arguments as NaN s. floatdouble数组中的NaN值可能无法正确处理,因为如果一个或两个参数均为NaN ,则比较将返回false。 With the naive bubble sort algorthm, they will stay in place, which may or may not be appropriate. 使用朴素的冒泡排序算法,它们将保留在原处,这可能是不合适的,也可能是不合适的。 With other sorting algorithms or just a slight variation of this one, the behavior may be different, possibly undefined. 使用其他排序算法或仅对该算法进行细微改动,其行为可能会有所不同,甚至可能不确定。

  • Your bubble sort implementation should use i = j + 1 for marginally better performance. 您的冒泡排序实现应使用i = j + 1来获得更好的性能。

  • The bubble sort algorithm is very inefficient for large arrays with a time complexity of O(N 2 ) . 对于时间复杂度为O(N 2 )的大型阵列,气泡排序算法的效率非常低。

Here is a more efficient approach where the sorting algorithm is left to the C library function qsort and a specific comparison function is written for each type: 这是一种更有效的方法,其中将排序算法留给C库函数qsort并为每种类型编写特定的比较函数:

int shortCmp(const void *aa, const void *bb) {
    short a = *(const short *)aa;
    short b = *(const short *)bb;
    return (b < a) - (a < b);
}

int intCmp(const void *aa, const void *bb) {
    int a = *(const int *)aa;
    int b = *(const int *)bb;
    return (b < a) - (a < b);
}

int floatCmp(const void *aa, const void *bb) {
    float a = *(const float *)aa;
    float b = *(const float *)bb;
    if (a != a || b != b) {
        /* sort NaN values to the end of the array */
        return (a != a) - (b != b);
    }
    return (b < a) - (a < b);
}

int doubleCmp(const void *aa, const void *bb) {
    double a = *(const double *)aa;
    double b = *(const double *)bb;
    if (a != a || b != b) {
        /* sort NaN values to the end of the array */
        return (a != a) - (b != b);
    }
    return (b < a) - (a < b);
}

//type 1:short, 2:int, 3:float, 4:double
void Sort(void *values, int nValues, int type) {
    switch (type) {
      case 1: //short
        qsort(values, nValues, sizeof(short), shortCmp);
        break;
      case 2: // int
        qsort(values, nValues, sizeof(int), intCmp);
        break;
      case 3: // float
        qsort(values, nValues, sizeof(float), floatCmp);
        break;
      case 4: // double
        qsort(values, nValues, sizeof(double), doubleCmp);
        break;
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM