您的位置:首页 > 编程语言

qsort对64位整数排序时遇到的坑

2015-11-16 21:26 211 查看
打开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
= (Cell*)malloc(sizeof(Cell));
ptr_array
->val = values
;
}

qsort(values, NUM, sizeof(int), simple_compare);
for (n = 0; n<6; n++)
printf("%d ", values
);
printf("\n");

qsort(ptr_array, NUM, sizeof(Cell*), complex_compare);
for (n = 0; n<6; n++)
printf("%d ", ptr_array
->val);
printf("\n");

for (n = 0; n<6; n++)
free(ptr_array
);

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了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  编程 技术