You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.15 10:58:05