C语言目录遍历程序中memmove()引发段错误问题求助
解决递归目录遍历中插入排序的段错误及逻辑问题
核心问题分析
- 参数类型不匹配导致段错误:你的
iSort函数定义接受的是struct info *fs_info[](指针数组),但调用时传入的是&fs_info——这里的fs_info是struct info类型的数组,它的地址类型是struct info (*)[N],和函数参数的指针数组类型完全不兼容。这会导致函数内部访问fs_info[j]时,错误地把数组元素当成指针来解引用,进而访问非法内存,触发EXC_BAD_ACCESS。 - 插入排序逻辑不完整:原代码只保存了
size作为key,排序后只恢复了size,完全没处理name字段,会导致文件名和大小不匹配;同时使用memmove的方式不符合插入排序的正确逻辑(插入排序应该是把当前元素暂存,然后把前面比它大的元素后移,最后把暂存的元素插入到正确位置)。 - 数据类型溢出风险:
st.st_size是off_t类型(通常是64位),而你用int存储,对于超过int范围的大文件,大小会被截断,导致数据错误。 - 错误处理冗余:
get_size中错误分支重复调用stat(path, &st),没必要,应该直接用第一次调用的返回值。 - 后续插入逻辑缺失:当数组填满后,你只调用了排序,但没有判断当前文件是否比数组中最小的文件大,也没有执行替换操作,无法更新最大的N个文件列表。
修正后的代码
#include <stdio.h> #include <dirent.h> #include <sys/stat.h> #include <string.h> // number of files to display size information for const int N = 10; // 修正size类型为off_t,避免大文件溢出 struct info { char name[1024]; off_t size; }; /* 修正参数类型:直接接受struct info数组的指针,而非指针数组 */ void iSort(struct info *fs_info, int size) { int i, j; struct info key; // 暂存整个结构体,而不是只存size for (i = 1; i < size; i++) { key = fs_info[i]; // 保存当前元素的完整信息 j = i - 1; // 把比key大的元素向后移动 while (j >= 0 && fs_info[j].size > key.size) { fs_info[j + 1] = fs_info[j]; // 直接结构体赋值,比memmove更直观 j = j - 1; } fs_info[j + 1] = key; // 把暂存的元素插入正确位置 } } void get_size(char *path, struct info fs_info[N]) { static int items_added = 0; struct stat st; int stat_ret = stat(path, &st); if (stat_ret == 0) { // 只处理普通文件,跳过目录(避免把目录大小算进去) if (!S_ISREG(st.st_mode)) { return; } if (items_added < N) { strncpy(fs_info[items_added].name, path, sizeof(fs_info[items_added].name)-1); fs_info[items_added].name[sizeof(fs_info[items_added].name)-1] = '\0'; // 确保字符串终止 fs_info[items_added].size = st.st_size; items_added++; } else { // 先排序,找到当前最小的文件 iSort(fs_info, N); // 如果当前文件比最小的大,替换它 if (st.st_size > fs_info[0].size) { strncpy(fs_info[0].name, path, sizeof(fs_info[0].name)-1); fs_info[0].name[sizeof(fs_info[0].name)-1] = '\0'; fs_info[0].size = st.st_size; // 重新排序,保持有序 iSort(fs_info, N); } } } else { printf("Error getting stat for entry %s: %d\n", path, stat_ret); } } void walk(const char *currDir, struct info fs_info[N]) { DIR *dir = opendir(currDir); struct dirent *entry; if (dir == NULL) { perror("opendir failed"); return; } while ((entry = readdir(dir)) != NULL) { if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { continue; } char path_to_entry[1024]; snprintf(path_to_entry, sizeof(path_to_entry), "%s/%s", currDir, entry->d_name); get_size(path_to_entry, fs_info); if (entry->d_type == DT_DIR) { walk(path_to_entry, fs_info); } } closedir(dir); } int main(int argc, char *argv[]) { if (argc != 2) { printf("Usage: %s <target directory>\n", argv[0]); return 1; // 错误退出码 } const char *target_dir = argv[1]; struct info fs_entries[N] = {0}; // 初始化所有字段为0,无需手动循环 printf("Finding %d largest files in: %s\n", N, target_dir); walk(target_dir, fs_entries); // 最后排序一次,从大到小输出 iSort(fs_entries, N); printf("\nTop %d largest files:\n", N); for (int i = N-1; i >= 0; i--) { if (fs_entries[i].size > 0) // 只输出有效条目 printf("%s : %lld bytes\n", fs_entries[i].name, (long long)fs_entries[i].size); } return 0; }
关键修正点说明
- 修正iSort参数与逻辑:
- 把函数参数改为
struct info *fs_info(直接指向结构体数组的指针),和调用时传入的数组名(会隐式转为指针)类型匹配。 - 插入排序时暂存整个
struct info结构体,确保文件名和大小一起移动,避免数据不匹配;直接用结构体赋值代替memmove,代码更简洁安全。
- 把函数参数改为
- 数据类型修正:将
struct info中的size改为off_t,并在输出时转为long long打印,避免大文件大小溢出。 - 完善文件过滤:在
get_size中判断S_ISREG(st.st_mode),只处理普通文件,跳过目录、设备文件等非普通文件。 - 完善填满后的逻辑:当数组满了之后,先排序找到最小的文件,若当前文件更大则替换,再重新排序,确保数组始终保存当前找到的最大N个文件。
- 优化初始化与错误处理:用
struct info fs_entries[N] = {0};直接初始化数组,避免手动循环;错误处理中使用perror打印opendir的错误原因,重复调用stat的问题也被修复。
内容的提问来源于stack exchange,提问作者John Harrington
相关产品推荐
相关产品推荐

