实现简易ls工具时遇realloc失效及乱码问题求助
我帮你梳理下代码里的几个关键问题,这些正是导致你遇到的错误和乱码的核心原因:
1. 未初始化指针触发realloc()错误
你定义的char** listofentries;没有初始化为NULL,第一次调用realloc(listofentries, ...)时传入的是一个野指针(随机内存地址),这直接违反了realloc的使用规则——它的第一个参数要么是NULL,要么是之前通过malloc/calloc/realloc分配的有效指针,所以才会抛出realloc(): invalid old size错误。
修复方法:初始化指针为NULL:
char** listofentries = NULL;
2. 错误复用entry->d_name指针导致未定义行为
readdir()返回的struct dirent结构体是被系统库复用的,每次调用readdir都会覆盖同一个结构体的内容。你直接把entry->d_name的指针存到数组里,最后所有元素都会指向同一块内存,最终只会保留最后一个文件名;而且当你调用closedir(dir)后,这块内存可能被系统回收,访问它就会触发无参数运行时的错误这类未定义行为。
修复方法:用strdup()复制文件名到新分配的独立内存中:
listofentries[counter] = strdup(entry->d_name);
(注意:strdup内部会调用malloc,后续要记得释放内存避免泄漏)
3. 路径拼接错误导致lstat失效(乱码根源)
当你遍历/bin/或/sbin/这类目录时,lstat(entryname, &s)中的entryname只是单纯的文件名(比如ls),lstat会在当前工作目录下查找这个文件,而不是你正在遍历的目标目录,这会导致stat获取的文件信息完全错误,进而输出错误的符号(所谓的乱码本质是文件类型判断错误,或者stat失败后st_mode是随机值)。
修复方法:拼接目标目录路径和文件名,生成完整路径后再调用lstat:
// 在for循环内拿到entryname后添加这段代码 char full_path[PATH_MAX]; snprintf(full_path, sizeof(full_path), "%s/%s", path, entryname); if (lstat(full_path, &s) == -1) { perror("lstat failed"); symbol = '?'; continue; }
需要额外包含头文件#include <limits.h>来使用PATH_MAX常量。
其他细节优化
- 内存泄漏:分配的
listofentries数组和每个strdup的字符串都需要在函数结束前释放,避免内存泄漏; - 错误检查:
opendir、realloc、strdup都可能失败,添加错误检查可以避免程序崩溃或出现奇怪行为; - 换行符问题:你代码里的
printf(" ")是笔误,应该改成printf("\n"),否则会编译错误。
修正后的完整代码
#include <dirent.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> #include <limits.h> #include <errno.h> int normalsort(const void *string1, const void *string2) { char *const *char1 = string1; char *const *char2 = string2; return strcasecmp(*char1, *char2); } int listDir(char* path, int listToggeled, int classifyToggeled) { DIR *dir = opendir(path); if (dir == NULL) { perror("opendir failed"); return errno; } struct dirent *entry; struct stat s; char** listofentries = NULL; char symbol = '-'; int counter = 0; while ((entry = readdir(dir)) != NULL) { if (entry->d_name[0] != '.') { // 安全地重新分配数组内存 char** temp = realloc(listofentries, (counter + 1) * sizeof(char*)); if (temp == NULL) { perror("realloc failed"); // 清理已分配的内存 for (int i = 0; i < counter; i++) { free(listofentries[i]); } free(listofentries); closedir(dir); return errno; } listofentries = temp; // 复制文件名到独立内存 listofentries[counter] = strdup(entry->d_name); if (listofentries[counter] == NULL) { perror("strdup failed"); // 清理内存 for (int i = 0; i < counter; i++) { free(listofentries[i]); } free(listofentries); closedir(dir); return errno; } counter++; } } qsort(listofentries, counter, sizeof(char*), normalsort); for (int i = 0; i < counter; i++) { char* entryname = listofentries[i]; if (entryname[0] != '.') { printf("%s", entryname); if (classifyToggeled == 1) { char full_path[PATH_MAX]; snprintf(full_path, sizeof(full_path), "%s/%s", path, entryname); if (lstat(full_path, &s) == -1) { perror("lstat failed"); symbol = '?'; } else { if (S_ISDIR(s.st_mode)) { symbol = '/'; } else if (S_ISLNK(s.st_mode)) { symbol = '@'; } else if ((S_ISREG(s.st_mode)) && (s.st_mode & S_IXUSR)) { symbol = '*'; } else { symbol = ' '; } } printf("%c", symbol); } if (listToggeled == 1) { printf("\n"); } else { printf(" "); } } } // 释放所有分配的内存 for (int i = 0; i < counter; i++) { free(listofentries[i]); } free(listofentries); closedir(dir); if (listToggeled == 0) { printf("\n"); } return 0; } int main(int argc, char **argv) { int classifyToggeled = 0; int listToggeled = 0; char* dirToList = "."; if (argc == 1) { listDir(dirToList, listToggeled, classifyToggeled); return 0; } for (int i = 1; i < argc; i++) { char* currentArg = argv[i]; // 检查-F参数 if (strcmp(currentArg, "-F") == 0 || strcmp(currentArg, "-1F") == 0 || strcmp(currentArg, "-F1") == 0) { classifyToggeled = 1; } // 检查-1参数 if (strcmp(currentArg, "-1") == 0 || strcmp(currentArg, "-1F") == 0 || strcmp(currentArg, "-F1") == 0) { listToggeled = 1; } // 处理目录参数 if (currentArg[0] != '-') { dirToList = currentArg; } } // 现在取消注释这行也不会报错了 // printf("dirtolist: %s\n", dirToList); listDir(dirToList, listToggeled, classifyToggeled); return 0; }
现在你可以测试一下:取消注释main里的printf行不会再报错,无参数运行正常,遍历/bin/和/sbin/时也能正确识别文件类型并输出对应的符号了。
内容的提问来源于stack exchange,提问作者Jocomol

