打开csdn各种浮夸的技术,我是个实用、实干主义,喜欢讨论些细节的,编程的珠玑。最近遇到一个坑跟大家分享下。
再简单不过的qsort
qsort函数的原型: void qsort(void * _Base, size_t _NumOfElements, size_t _SizeOfElements,
int(* _PtFuncCompare)(const void *, const void *));
- _Base是数组指针
- _NumOfElements 是每个元素的长度,
- _SizeOfElements 是数组中的元素个数,
- _PtFuncCompare是元素之间的比较函数,该函数的返回值是int。 PtFuncCompare函数的两个void指针参数,传的是数组中每个元素的地址。如果元素的基本类型,如int,则函数参数是int*,指向int的元素;如果元素类型是结构体指针,则函数参数是结构体指针的地址,即指针的指针,二级指针。
qsort示例
/* qsort example */
typedef struct _Cell
{
int val;
int other_stuff[1024];
}Cell;
int complex_compare(const void * a, const void * b)
{
Cell* p1 = *((Cell**)a);
Cell* p2 = *((Cell**)b);
return p1->val - p2->val;
}
int simple_compare(const void * a, const void * b)
{
return (*(int*)a - *(int*)b);
}
int test_main()
{
int values[] = { 40, 10, 100, 90, 20, 25 };
const int NUM = sizeof(values) / sizeof(values[0]);
Cell* ptr_array[NUM];
int n;
for (n = 0; n < NUM; n++)
{
ptr_array[n] = (Cell*)malloc(sizeof(Cell));
ptr_array[n]->val = values[n];
}
qsort(values, NUM, sizeof(int), simple_compare);
for (n = 0; n<6; n++)
printf("%d ", values[n]);
printf("\n");
qsort(ptr_array, NUM, sizeof(Cell*), complex_compare);
for (n = 0; n<6; n++)
printf("%d ", ptr_array[n]->val);
printf("\n");
for (n = 0; n<6; n++)
free(ptr_array[n]);
return 0;
}
简单的qsort示例,引自 cplusplus 网站的示例,simple_compare函数采用了很简洁,帅气的写法,将a,b两个void指针转化成int指针然后做差。。。qsort的比较回调函数返回值是int,而不是bool,所以做差是一种很合适的做法,避免了很挫的if else比较。
复杂的例子,如Cell结构体指针数组的qsort,complex_compare函数,a,b是Cell类型的二级指针,比较的时候需要最终获取Cell内部的val,然后做差。
好吧,各种版本都是做差。。。
自己埋下的坑
排序int64
最近项目中的id采用了int64,因此照猫画虎地搞了一个qsort版本,
int bad_cmp(const void* a, const void* b)
{
const int64_t p1 = *(const int64_t*)a;
const int64_t p2 = *(const int64_t*)b;
return p1 - p2;
}
使用上面的函数对int64数组进行排序,上线前测试没问题,上线了一个多月也没啥问题。。哈哈~ 看出问题了么?
突然有一天,产品报了个bug,追啊追啊花了两天。最终发现最近新上的一批人工数据,其中的ID采用了很多的数值,然后引擎对int64数值qsort排序,结果序列不是有序的。。。
下面是抓取到的当时一个排序结果子集:
4110253516 4110253516 4110253517 4110253517 4110253518 4110253519 1384161 1384161 1384163 1393169 1393171 1393171 1393172 1393172 1393173 1393173 1393221 1393223 1393228 1393229
排序结果分成了两段,前面有序都非常大,后面有序都很小。。。最终反应过来了,就出在compare比较函数,返回值是int,两个int64做差,如4110253517 - 1384161,结果超过了int范围,从而成为了很小的负数,比较结果是4110253517 < 1384161。
解决方法:
int good_cmp(const void* a, const void* b)
{
const int64_t p1 = *(const int64_t*)a;
const int64_t p2 = *(const int64_t*)b;
if (p1 < p2)
return -1;
else if (p1 > p2)
return 1;
return 0;
}
虽然很挫的写法,但是非常保险使用,没有溢出的问题。。。if else分支,有个优化的tips:概率越大的写的越靠前面,从而快速过滤绝大部分情况。
这可能也是为啥STL中的sort函数,传入的仿函数返回值是bool而不是int了。