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

实现简易ls工具时遇realloc失效及乱码问题求助

分析与修复你的简易ls工具问题

我帮你梳理下代码里的几个关键问题,这些正是导致你遇到的错误和乱码的核心原因:

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 17:01:46