在此问题中,使用void
指向数组的指针,元素数量和指示元素类型的整数的函数应该对数组进行排序。有什么技巧可以避免像下面的解决方案那样重复编写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;
}
}
}
}
答案 0 :(得分:6)
是的,还有另一种选择。在名为qsort
的{{3}}中(这可能是快速排序的缩写,但也可以是其他任何排序算法)。要使用该函数,请向其传递数组,数组中元素的数量,每个元素的大小以及比较函数。
您只需要为每种数据类型编写比较功能,如该页面上的示例所示。
如果要将4个比较功能合并为一个功能,则必须将一些上下文信息传递给比较功能。在这种情况下,您不能再使用qsort
,而必须使用qsort_s
。然后您的比较函数可能如下所示:
#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);
}
这是一些相当高级的内容:
但是最后,只要它们支持<
运算符,就可以在列表中添加更多类型。
请注意,float
和double
甚至都不属于此类别,因为一旦其中一个数字为{{1},它们的运算符<
就会返回false
。 },表示“非数字”,它来自NaN
之类的表达式。一旦在数组中具有这样的值,该行为就变得不确定。排序功能甚至可能陷入无限循环。要解决此问题,请更改0.0 / 0.0
宏的定义:
compare
现在看起来更加复杂,但适用于#define compare(a, b) (((a) > (b)) - !((b) <= (a)))
。例如:
NaN
这意味着NaN将被排序到数组的前面。
compare(NaN, 5)
= (NaN > 5) - !(5 <= NaN)
= false - !(5 <= NaN)
= false - !(false)
= false - true
= 0 - 1
= -1
该死。比较两个NaN应该得出0,表示它们相等。因此,在这种特殊情况下,需要进行更正:
compare(NaN, NaN)
= (NaN > NaN) - !(NaN <= NaN)
= false - true
= -1
由于#define compare(a, b) (((a) > (b)) - ((b) > (a)) - ((a) != (a) || (b) != (b)))
是唯一一个不等于其自身的值,因此此附加代码不会影响整数算术,应由编译器对其进行优化。
答案 1 :(得分:0)
这里有两种不同的方法可以避免代码重复出现:
如果您不能使用库函数或想保留算法,则可以使用预处理器宏来解决此问题:
#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;
}
}
注意:
宏SORT_TYPE
可以被仅具有一次评估的副作用的参数调用,但是它仍然是脆弱的:如果以变量名的形式表示参数,生成的代码将会中断ptr
,i
,j
或n
。可以通过命名块变量ptr__
或其他一些扭曲的方式来尝试减少冲突,但是并不能完全解决问题。编写宏时必须格外注意并小心使用。
NaN
和float
数组中的 double
值可能无法正确处理,因为如果一个或两个参数均为NaN
,则比较将返回false。使用朴素的冒泡排序算法,它们将保留在原处,这可能是不合适的,也可能是不合适的。在使用其他排序算法或对此算法进行细微改动后,其行为可能会有所不同,甚至可能是不确定的。
您的冒泡排序实现应使用i = j + 1
来获得更好的性能。
气泡排序算法对于时间复杂度为 O(N 2 )的大型阵列而言效率非常低。
这是一种更有效的方法,其中将排序算法留给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;
}
}