关于qsort比较函数使用减法实现的正确性疑问及测试
为什么qsort比较函数用减法实现会踩坑?
你这个观察太关键了!很多人图省事,在qsort的比较函数里直接用*(int*)a - *(int*)b返回结果,但这种写法暗藏严重的溢出风险,尤其是碰到INT_MIN(也就是-2147483648)的时候,直接就会出问题。
举个你提到的例子:当数组里有INT_MIN,比如int nums[]={-2147483648,1,2,3},当比较1和INT_MIN时,1 - INT_MIN的数学结果是2147483649,但int类型的最大值只有2147483647,这就触发了有符号整数溢出——这属于C语言里的未定义行为,程序可能返回错误的排序结果,甚至出现奇怪的崩溃。
我们可以用一段测试代码验证这个问题:
#include <stdio.h> #include <limits.h> #include <stdlib.h> int compare (const void * a, const void * b) { return ( *(int*)a - *(int*)b ); } int main(void) { int nums[] = {INT_MIN, 1, 2, 3}; int n = sizeof(nums)/sizeof(nums[0]); // 用有问题的比较函数排序 qsort(nums, n, sizeof(int), compare); printf("排序结果:"); for(int i=0; i<n; i++){ printf("%d ", nums[i]); } printf("\n"); // 单独看比较逻辑的问题 int a_val = 1; int b_val = INT_MIN; printf("1 - INT_MIN 的计算结果:%d\n", a_val - b_val); return 0; }
运行这段代码你会发现,1 - INT_MIN的输出不是预期的正数,而是一个负数(比如在大多数编译器上会输出-2147483647),这就导致qsort错误地认为1比INT_MIN小,最终排序结果完全错乱。
那正确的写法应该是什么样的?给你两种安全的方案:
方案一:用条件判断明确返回值
最直观的写法,完全避免溢出:
int compare(const void *a, const void *b) { const int x = *(const int*)a; const int y = *(const int*)b; if (x < y) return -1; if (x > y) return 1; return 0; }
方案二:用简洁的布尔运算写法(C99及以上适用)
这个写法很巧妙,利用布尔值的隐式转换(true为1,false为0),同样不会有溢出问题:
int compare(const void *a, const void *b) { const int x = *(const int*)a; const int y = *(const int*)b; return (x > y) - (x < y); }
总结一下:永远不要在qsort的比较函数里用减法返回结果,只要两个整数的差值超过int的范围,就会触发未定义行为。用条件判断或者上述的布尔运算写法,才是安全可靠的做法。
内容的提问来源于stack exchange,提问作者52coder
相关产品推荐
相关产品推荐

