使用K&R《C程序设计语言》示例代码排序整数数组时出现SEGFAULT的原因及通用修复方案咨询
K&R《C程序设计语言》示例代码排序整数数组时出现SEGFAULT的原因及通用修复方案咨询
嗨,我来帮你揪出这个段错误的根源,再给你一套通用的修复方案——毕竟谁不想一个排序函数搞定所有类型呢😉
问题核心原因
你遇到的段错误,本质是K&R书中的这个qsort实现天生就是为指针数组设计的,而你强行把整数数组转成void**传入,完全违背了它的设计逻辑:
- 当排序字符串数组
char* s_strings[]时,数组里的每个元素本身就是指针,swap函数交换的是这些指针的位置,完全合法。 - 但整数数组
int i_numbers[]里存的是实打实的整数值(比如INT_MAX、5这些),你把它强转成void**后,swap会把这些整数当成内存地址来读写——想象一下,把INT_MAX(一个远超合法内存范围的数)当作指针去访问,操作系统直接就给你抛出段错误了,这太正常了。
通用修复方案(让一个qsort通吃所有类型)
要让这个排序函数既能处理字符串数组,又能处理整数数组,我们需要参考标准库qsort的设计思路,给函数增加元素大小参数,同时修改交换和比较逻辑:
1. 重写swap函数:按字节交换元素
原来的swap只懂交换指针,现在我们要改成按元素的字节大小来复制交换,这样不管是int、char*还是其他自定义类型都能处理:
#include <string.h> // 需要用到memcpy static void swap(void *base, size_t elem_size, int left, int right) { // 临时缓冲区,存一个元素的全部内容 unsigned char temp[elem_size]; // 计算左右元素的内存地址 unsigned char *left_ptr = (unsigned char*)base + left * elem_size; unsigned char *right_ptr = (unsigned char*)base + right * elem_size; // 三步完成交换 memcpy(temp, left_ptr, elem_size); memcpy(left_ptr, right_ptr, elem_size); memcpy(right_ptr, temp, elem_size); }
2. 修改qsort函数:支持任意元素类型
给qsort增加elem_size参数,同时调整比较函数的调用方式——现在传递给比较函数的是指向元素的指针,而不是元素本身:
void qsort(void *base, int left, int right, size_t elem_size, int (*compare)(const void *, const void*)) { int count, last; if (left >= right) return; // 空数组或单元素数组直接返回 // 把中间元素交换到左边作为基准 swap(base, elem_size, left, (left + right)/2); last = left; for (count = left + 1; count <= right; count++) { // 传递当前元素和基准元素的指针给比较函数 if ((*compare)((unsigned char*)base + count * elem_size, (unsigned char*)base + left * elem_size) < 0) { swap(base, elem_size, ++last, count); } } // 把基准元素放到正确的位置 swap(base, elem_size, left, last); // 递归排序左右子数组 qsort(base, left, last - 1, elem_size, compare); qsort(base, last + 1, right, elem_size, compare); }
3. 调整比较函数:适配指针参数
比较函数现在要接收const void*类型的参数,然后根据元素类型转成对应的指针再取值比较:
// 整数比较函数 int cmpint(const void *a, const void *b) { const int *left = (const int*)a; const int *right = (const int*)b; return (*left > *right) - (*left < *right); // 简洁的三态返回 } // 字符串比较函数(注意:字符串数组的元素是char*,所以我们要先解引用拿到字符串指针) int cmpstr(const void *a, const void *b) { const char *left = *(const char**)a; const char *right = *(const char**)b; return strcmp(left, right); }
4. 调整main函数中的调用
现在调用qsort时,需要传入元素的大小:
int main(void) { int i_numbers[] = {INT_MAX, 5, 9, 4, 8, 3, 7, 10, 2, 1, 6, INT_MIN}; char *s_strings[] = { "One", "Two", "Three", "Four", "Five", "", "Seven", "Eight", "Nine" }; int i_size; // 字符串数组排序 i_size = sizeof(s_strings)/sizeof(s_strings[0]); prtstr (s_strings, i_size); qsort(s_strings, 0, i_size -1, sizeof(char*), cmpstr); prtstr (s_strings, i_size); // 整数数组排序 i_size = sizeof(i_numbers)/sizeof(i_numbers[0]); prtint (i_numbers, i_size); qsort(i_numbers, 0, i_size -1, sizeof(int), cmpint); prtint (i_numbers, i_size); }
额外小提醒
原来的代码里你的qsort和标准库的qsort重名了,虽然在这个程序里没出问题,但实际开发中最好改个名字(比如kr_qsort),避免冲突。
备注:内容来源于stack exchange,提问作者Mike T.
相关产品推荐
相关产品推荐

