无需辅助函数转换函数指针参数:如何直接向qsort传递strcmp?
strcmp传给qsort? 答案是:不能直接传递,哪怕你做了强制类型转换,也无法正确排序字符串数组,同时还会触发C标准中的未定义行为。下面具体拆解原因,以及正确的做法:
1. 参数层级与类型的双重不匹配
先明确两个函数的核心差异:
strcmp的原型是:int strcmp(const char *str1, const char *str2);—— 它接收两个字符串指针,直接比较指针指向的字符串内容。qsort要求的比较函数原型是:int (*compar)(const void *, const void*);—— 它接收两个指向数组元素的指针,而不是元素本身。
举个实际场景:如果你要排序的是字符串数组char *strs[] = {"apple", "banana", "cherry"},数组的每个元素是char*类型的字符串指针。此时qsort调用比较函数时,会传入&strs[0]和&strs[1]——也就是两个const char**类型的指针(被转换成const void*)。
如果你像代码里那样强制转换strcmp的类型并传入,strcmp会把这两个const char**直接当成const char*来处理:它不会去读取指针指向的"apple"或"banana",反而会把char*指针本身的内存地址值当成字符串来比较,这必然会得到完全错误的排序结果。
2. 强制类型转换带来的未定义行为
退一步说,哪怕忽略参数层级的问题,C标准也明确规定:通过不兼容的函数指针类型调用函数,结果是未定义的。strcmp的参数是const char*,而qsort要求的比较函数参数是const void*,这两种函数指针类型并不兼容。
虽然在x86等常见架构下,void*和char*的内存表示方式一致,强制转换后可能碰巧能运行,但这完全依赖平台实现——换个编译器、换个特殊架构(比如某些嵌入式平台),程序可能直接崩溃,或者出现无法预测的异常结果。
3. 正确且安全的做法:写一个极简辅助函数
其实只需要3行代码就能解决问题,既符合C标准,又清晰易懂:
int compare_strings(const void *a, const void *b) { // 将void*转为指向char*的指针,解引用后得到真正的字符串指针,再传给strcmp return strcmp(*(const char **)a, *(const char **)b); }
调用qsort时直接传递这个辅助函数即可:
qsort(strs, sizeof(strs)/sizeof(strs[0]), sizeof(char*), compare_strings);
额外补充:如果是排序单个字符数组?
如果你的需求是排序像char chars[] = "dcba"这样的单个字符数组(元素是char),strcmp同样不适用——因为它是用来比较字符串的。此时你需要写一个比较单个字符的辅助函数:
int compare_chars(const void *a, const void *b) { return *(const char*)a - *(const char*)b; }
内容的提问来源于stack exchange,提问作者user18989277

