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

如何编写C程序递归打印指定目录的文件及子目录树结构?

Hey Antonio, let's build this directory tree printer in C together. I've implemented similar tools before, so I'll break down every part so you understand how it works, not just copy-paste code.

Required Headers

First, we need these headers to access system calls for directory handling, file status checks, and string operations:

#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/stat.h>
#include <string.h>
#include <errno.h>
#include <limits.h> // For PATH_MAX

Step 1: Validate Command-Line Input

Our program needs exactly one argument: the target directory. Let's start by making sure the user provides it correctly:

void print_tree(const char *path, int depth, int *is_last);

int main(int argc, char *argv[]) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <directory_path>\n", argv[0]);
        return EXIT_FAILURE;
    }

    // Print the root directory first to kick off the tree
    printf("%s\n", argv[1]);
    print_tree(argv[1], 0, NULL);

    return EXIT_SUCCESS;
}

Step 2: The Recursive Tree Printing Function

This is the heart of the program. It handles opening directories, filtering out special entries, printing formatted lines, and recursing into subdirectories. The is_last array tracks which parent entries were the last in their directory—this lets us draw the correct vertical lines for the tree structure.

Here's the full function with comments explaining each part:

void print_tree(const char *path, int depth, int *is_last) {
    // Open the target directory
    DIR *dir = opendir(path);
    if (!dir) {
        fprintf(stderr, "Failed to open directory %s: %s\n", path, strerror(errno));
        return;
    }

    struct dirent *entry;
    int entry_count = 0;

    // First pass: count valid entries (skip . and .. to avoid infinite recursion)
    while ((entry = readdir(dir)) != NULL) {
        if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) {
            entry_count++;
        }
    }
    rewinddir(dir); // Reset directory pointer to start processing entries

    // Allocate array to track if entries are the last in their directory
    int *new_is_last = malloc((depth + 1) * sizeof(int));
    if (!new_is_last) {
        fprintf(stderr, "Memory allocation error: %s\n", strerror(errno));
        closedir(dir);
        return;
    }

    // Copy parent's is_last status if we're not at the root level
    if (depth > 0 && is_last != NULL) {
        memcpy(new_is_last, is_last, depth * sizeof(int));
    }

    int current_entry = 0;
    while ((entry = readdir(dir)) != NULL) {
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
            continue;
        }

        current_entry++;
        int is_last_entry = (current_entry == entry_count);
        new_is_last[depth] = is_last_entry;

        // Print indentation and vertical lines for tree structure
        for (int i = 0; i < depth; i++) {
            printf(is_last[i] ? "    " : "│   ");
        }

        // Print entry marker (└── for last entries, ├── for others)
        printf(is_last_entry ? "└── " : "├── ");
        printf("%s\n", entry->d_name);

        // Build full path to the child entry
        char child_path[PATH_MAX];
        int snprintf_result = snprintf(child_path, sizeof(child_path), "%s/%s", path, entry->d_name);
        if (snprintf_result >= sizeof(child_path) || snprintf_result < 0) {
            fprintf(stderr, "Path too long or invalid for %s\n", entry->d_name);
            continue;
        }

        // Check if the entry is a directory to trigger recursion
        struct stat stat_buf;
        if (stat(child_path, &stat_buf) == -1) {
            fprintf(stderr, "Failed to get info for %s: %s\n", child_path, strerror(errno));
            continue;
        }

        if (S_ISDIR(stat_buf.st_mode)) {
            print_tree(child_path, depth + 1, new_is_last);
        }
    }

    // Clean up allocated memory and close the directory
    free(new_is_last);
    closedir(dir);
}

Step 3: Understanding the Tree Formatting

The trickiest part is getting the vertical lines right. Here's how it works:

  • The is_last array tracks, for each depth level, whether that directory entry was the last one in its parent.
  • For indentation: if a parent entry wasn't the last, we print │ to keep the vertical line going. If it was the last, we print (spaces) to break the line.
  • For entry markers: ├── for non-last entries, └── for the final entry in a directory.

How to Compile & Test

  1. Save the code as tree.c
  2. Compile with GCC: gcc tree.c -o tree
  3. Run it on a directory: ./tree /path/to/your/directory

You'll get output like this:

./test_dir
├── docs
│   ├── README.md
│   └── notes.txt
├── src
│   ├── main.c
│   └── utils.c
└── Makefile

Notes & Troubleshooting

  • PATH_MAX: This is a system-defined maximum path length. If your system doesn't define it, replace it with a reasonable value like 1024.
  • Permission Errors: If the program can't open a directory, it'll print an error but continue processing other entries.
  • Memory Leaks: We use malloc for the new_is_last array and free it at the end of the function to avoid leaks.
  • Cross-Platform: This uses POSIX functions (opendir, readdir, stat), so it works on Linux, macOS, and other Unix-like systems. For Windows, you'd need to use Win32 API functions instead.

内容的提问来源于stack exchange,提问作者Antonio

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 07:19:49