如何修复qsort比较器引发的段错误问题
我来帮你排查代码里的几个关键问题,正是这些问题触发了段错误:
1. qsort的元素个数参数完全错误
你用int i = sizeof(AllRecords);把数组总字节数传给了qsort的第二个参数,但qsort的第二个参数需要的是元素的数量,不是总字节数。sizeof(AllRecords)得到的是file_size * 100,而实际有效的元素个数是你计算的number_of_records = file_size / 100。用错误的元素个数会让qsort访问超出数组范围的内存,直接触发段错误。
2. fread的参数顺序错误
fread的调用格式是fread(ptr, 单个元素大小, 元素数量, 流),你现在写的fread(AllRecords, 100, file_size, file)会尝试读取file_size个100字节的元素,总共要读100*file_size字节,远超过文件实际大小,会导致数组越界写入,破坏栈内存。正确的应该是用number_of_records作为要读取的元素个数。
3. 栈上分配过大数组的风险
char AllRecords[file_size][100];是在栈上分配数组,栈的空间通常只有几MB(比如Linux下默认是8MB),如果你的文件比较大(比如超过80KB,8MB/100=81920),就会触发栈溢出,直接导致段错误。应该用动态内存分配(malloc/calloc)来分配堆内存,避免栈溢出。
4. compare函数的类型转换问题
qsort传给compare函数的r1和r2是指向数组元素的指针(也就是char (*)[100]类型),直接传给strcmp(需要const char*)最好显式转换,避免类型不匹配导致的未定义行为。
修正后的代码
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <assert.h> #include <ctype.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include "sort.h" // 正确的compare函数:显式转换为const char* int compare(const void *r1, const void *r2) { return strcmp((const char*)r1, (const char*)r2); } int main(int argc, char *argv[]) { // 检查命令行参数是否足够 if (argc != 3) { fprintf(stderr, "Usage: %s <input_file> <output_file>\n", argv[0]); exit(EXIT_FAILURE); } FILE *file = fopen(argv[1], "r"); if (!file) { perror("Failed to open input file"); exit(EXIT_FAILURE); } struct stat fs; if (stat(argv[1], &fs) == -1) { perror("Failed to get file stats"); fclose(file); exit(EXIT_FAILURE); } int file_size = fs.st_size; int number_of_records = file_size / 100; // 检查文件大小是否是100的整数倍,避免处理不完整记录 if (file_size % 100 != 0) { fprintf(stderr, "File size is not a multiple of 100 bytes\n"); fclose(file); exit(EXIT_FAILURE); } // 动态分配堆内存,避免栈溢出 char (*AllRecords)[100] = malloc(number_of_records * sizeof(*AllRecords)); if (!AllRecords) { perror("Failed to allocate memory"); fclose(file); exit(EXIT_FAILURE); } // 正确读取number_of_records个100字节的元素 size_t read_count = fread(AllRecords, 100, number_of_records, file); if (read_count != number_of_records) { fprintf(stderr, "Failed to read all records\n"); free(AllRecords); fclose(file); exit(EXIT_FAILURE); } fclose(file); // qsort参数:元素个数是number_of_records,每个元素大小100字节 qsort(AllRecords, number_of_records, 100, compare); FILE *file2 = fopen(argv[2], "w"); if (!file2) { perror("Failed to open output file"); free(AllRecords); exit(EXIT_FAILURE); } // 写入正确数量的元素 size_t write_count = fwrite(AllRecords, 100, number_of_records, file2); if (write_count != number_of_records) { fprintf(stderr, "Failed to write all records\n"); free(AllRecords); fclose(file2); exit(EXIT_FAILURE); } fclose(file2); // 释放动态分配的内存,避免内存泄漏 free(AllRecords); return EXIT_SUCCESS; }
额外的改进点
- 增加了命令行参数检查,避免用户输入错误
- 增加了文件打开、stat、malloc等操作的错误处理,让程序更健壮
- 检查文件大小是否是100的整数倍,避免处理不完整的记录
- 使用
EXIT_SUCCESS/EXIT_FAILURE替代魔法数字,代码更易读 - 释放了动态分配的内存,避免内存泄漏
内容的提问来源于stack exchange,提问作者Bryce

