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

打开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了。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值