qsort排序ProcEntry指针数组顺序异常问题求助
问题分析与解决
我尝试用qsort对ProcEntry指针数组进行升序排序,但部分指针位置不符合预期,试了多种defaultSort比较器实现都没解决问题,相关代码如下:
#define _GNU_SOURCE #include <stdio.h> #include <errno.h> #include <stdlib.h> #include <dirent.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <ctype.h> #include "ProcEntry.h" #define MAX_PATH_LENGTH 4096 #define UNUSED(x) (void)x static int defaultFilter(const struct dirent *current){ return isdigit(current->d_name[0]); } static int defaultSort(const void * procEntryPtr1, const void * procEntryPtr2){ /* Dereference the parameters to access the ProcEntry pointers */ ProcEntry *ProcEntry1 = *(ProcEntry **)procEntryPtr1; ProcEntry *ProcEntry2 = *(ProcEntry **)procEntryPtr2; // if((ProcEntry1)->pid < (ProcEntry2)->pid) return -1; // if((ProcEntry1)->pid == (ProcEntry2)->pid) return 0; // if((ProcEntry1)->pid > (ProcEntry2)->pid) return 1; return (ProcEntry2->pid > ProcEntry1->pid) - (ProcEntry2->pid < ProcEntry1->pid); //return ProcEntry1 < ProcEntry2 ? -1 : ProcEntry1 > ProcEntry2 ? 1 : 0; } int main (int argc, char * argv[]) { struct dirent **namelist; int n; int opt; int print = 0; // 0 for no 1 for yes /* Declare filterFunction pointer */ int(*filterFunction)(const struct dirent *); filterFunction = defaultFilter; /* Declare filterFunction pointer */ int(*sortFunction)(const void *, const void *); sortFunction = defaultSort; char* dir = "/proc"; /* Variable Fields */ while((opt = getopt(argc, argv, "d:pczh")) != -1){ switch(opt){ case 'd': dir = argv[2]; if(strcmp(dir, "/proc") == 0){ filterFunction = defaultFilter; } else { filterFunction = NULL; } print = 1; break; case 'p': //Part 3 print = 1; filterFunction = defaultFilter; sortFunction = defaultSort; break; case 'c': //Part 3 break; case 'z': //Part 3 break; case 'h': print = 0; printf("Usage: %s [-d <path>] [-p] [-c] [-z] [-h]\n", argv[0]); printf("\t-d <path> Directory containing proc entries (default: /proc)\n"); printf("\t-p Display proc entries sorted by pid (default)\n"); printf("\t-c Display proc entries sorted by command lexicographically\n"); printf("\t-z Display ONLY proc entries in the zombie state \n"); printf("\t-h Display this help message\n"); break; default: fprintf(stderr,"Error: Invalid Option Specified\n"); fprintf(stderr, "Usage: %s [-d <path>] [-p] [-c] [-z] [-h]\n", argv[0]); printf("\t-d <path> Directory containing proc entries (default: /proc)\n"); printf("\t-p Display proc entries sorted by pid (default)\n"); printf("\t-c Display proc entries sorted by command lexicographically\n"); printf("\t-z Display ONLY proc entries in the zombie state \n"); printf("\t-h Display this help message\n"); } } if(argc > 6){ fprintf(stderr, "Usage: ./myps [-d <path>] [-p] [-c] [-z] [-h]\n"); printf("\t-d <path> Directory containing proc entries (default: /proc)\n"); printf("\t-p Display proc entries sorted by pid (default)\n"); printf("\t-c Display proc entries sorted by command lexicographically\n"); printf("\t-z Display ONLY proc entries in the zombie state \n"); printf("\t-h Display this help message\n"); } n = scandir(dir, &namelist, filterFunction, alphasort); qsort(namelist, n, sizeof(ProcEntry *), sortFunction); if (n < 0) perror("scandir"); else { while (n--) { printf("%s\n", namelist[n]->d_name); free(namelist[n]); } free(namelist); } /* display the names on stdout stream */ if(print == 1){ print = 0; int i; for (i = 0; i < n; ++i) { fprintf(stdout,"%s\n", namelist[i]->d_name); } } return 0; }
核心错误点
类型完全不匹配:
scandir返回的namelist是struct dirent **类型数组,每个元素是指向struct dirent的指针,但defaultSort函数却把这些指针强制转换成ProcEntry **并访问pid成员,属于非法内存访问,行为完全未定义,必然导致排序异常或程序崩溃。qsort的第三个参数错误:应传入sizeof(struct dirent *),而非sizeof(ProcEntry *),否则qsort计算元素偏移时会出错。
排序逻辑前提错误:
尚未将struct dirent转换为ProcEntry就直接排序,namelist中根本不存在ProcEntry对象。若要按PID排序,要么直接在排序函数里解析d_name(/proc下的目录名即PID字符串)为整数比较,要么先生成ProcEntry数组再排序。打印逻辑错误:
在第一个while(n--)循环中,n已被递减至0,后续for循环的i < n条件永远不成立,不会执行任何打印。
修正方案
方案1:直接基于struct dirent按PID排序
修改defaultSort和qsort调用:
static int defaultSort(const void *a, const void *b) { // a和b是struct dirent*的指针 const struct dirent *dirent1 = *(const struct dirent **)a; const struct dirent *dirent2 = *(const struct dirent **)b; // 将目录名(PID字符串)转换成整数 pid_t pid1 = atoi(dirent1->d_name); pid_t pid2 = atoi(dirent2->d_name); // 升序排序:pid1 < pid2返回-1,相等返回0,否则返回1 if (pid1 < pid2) return -1; if (pid1 > pid2) return 1; return 0; } // 在main里修改qsort调用: qsort(namelist, n, sizeof(struct dirent *), defaultSort);
方案2:先构建ProcEntry数组再排序
若确实需要使用ProcEntry,需先遍历namelist创建ProcEntry对象,再对数组排序:
// 假设ProcEntry结构体包含pid字段,且有创建/释放函数 int main() { // ... 其他代码 ... n = scandir(dir, &namelist, filterFunction, alphasort); if (n < 0) { perror("scandir"); return 1; } // 分配ProcEntry指针数组 ProcEntry **procEntries = malloc(n * sizeof(ProcEntry *)); if (!procEntries) { perror("malloc"); while (n--) free(namelist[n]); free(namelist); return 1; } // 填充ProcEntry数组 for (int i = 0; i < n; i++) { pid_t pid = atoi(namelist[i]->d_name); procEntries[i] = createProcEntry(pid); // 假设存在该创建函数 free(namelist[i]); } free(namelist); // 排序ProcEntry数组 qsort(procEntries, n, sizeof(ProcEntry *), defaultSort); // 打印并释放资源 for (int i = 0; i < n; i++) { printf("%d\n", procEntries[i]->pid); freeProcEntry(procEntries[i]); // 假设存在该释放函数 } free(procEntries); // ... 其他代码 ... }
修正打印逻辑
若保留原打印逻辑,需先保存n的初始值,且注意不能访问已释放的namelist:
int original_n = n; // 先处理打印再释放资源 if(print == 1){ for (int i = 0; i < original_n; ++i) { fprintf(stdout,"%s\n", namelist[i]->d_name); } } // 再释放namelist while (original_n--) { free(namelist[original_n]); } free(namelist);
内容的提问来源于stack exchange,提问作者Kaden Baratcart
相关产品推荐
相关产品推荐

