C语言目录树生成程序全局word数组被覆盖问题及排序需求解决咨询
Let's tackle your problems one by one—first the broken filename storage in the word array, then the case-insensitive sorting feature you're aiming for.
1. Why Your word Array Is Overwriting Filenames
The core issue here is how you're storing filenames in the word array:
word[coun] = ("%s", entry->d_name);
This line uses a comma expression, which effectively just assigns the pointer of entry->d_name to word[coun]. The problem? The entry struct returned by readdir() gets reused every loop iteration, and when you recurse into subdirectories, the memory entry->d_name points to gets overwritten with new filenames. By the end of your program, every element in word is pointing to the same memory block, which holds the last filename processed—hence the overwritten entries you're seeing.
Fix for Filename Storage
Instead of storing pointers, you need to copy the actual string data into new memory. Use strdup() (which handles malloc + strcpy for you) to create a duplicate of each filename:
word[coun] = strdup(entry->d_name); // Always check for allocation failures! if (word[coun] == NULL) { perror("strdup failed"); exit(EXIT_FAILURE); } coun++;
Also, don't forget to free this allocated memory at the end of main() to avoid leaks:
for (int i = 0; i < coun; ++i) { free(word[i]); } free(word);
2. Better Approach: Case-Insensitive Sorting Per Directory
Storing all filenames in a global array isn't ideal for tree sorting—you want each directory's contents to be sorted independently (which makes the tree structure logical). Here's a cleaner implementation that sorts each directory's entries (directories and files) in case-insensitive order, then builds the tree:
Updated Full Code
#include <unistd.h> #include <stdio.h> #include <dirent.h> #include <string.h> #include <sys/stat.h> #include <stdlib.h> #include <ctype.h> // Struct to hold entry info: name and whether it's a directory typedef struct { char *name; int is_dir; } DirEntry; // Case-insensitive comparison function for qsort int compare_entries(const void *a, const void *b) { const DirEntry *entry_a = (const DirEntry *)a; const DirEntry *entry_b = (const DirEntry *)b; // Use strcasecmp for case-insensitive comparison (POSIX standard) return strcasecmp(entry_a->name, entry_b->name); } void printdir(char *dir, int depth) { DIR *dp; struct dirent *entry; struct stat statbuf; DirEntry *entries = NULL; int entry_count = 0; if ((dp = opendir(dir)) == NULL) { fprintf(stderr, "cannot open directory: %s\n", dir); return; } chdir(dir); // Step 1: Collect all entries in the current directory while ((entry = readdir(dp)) != NULL) { lstat(entry->d_name, &statbuf); // Skip . and .. if (strcmp(".", entry->d_name) == 0 || strcmp("..", entry->d_name) == 0) { continue; } // Allocate space for this entry entries = realloc(entries, (entry_count + 1) * sizeof(DirEntry)); if (entries == NULL) { perror("realloc failed"); exit(EXIT_FAILURE); } // Store entry details entries[entry_count].name = strdup(entry->d_name); entries[entry_count].is_dir = S_ISDIR(statbuf.st_mode); entry_count++; } // Step 2: Sort entries case-insensitively qsort(entries, entry_count, sizeof(DirEntry), compare_entries); // Step 3: Print sorted entries and recurse into directories for (int i = 0; i < entry_count; i++) { if (entries[i].is_dir) { printf("- %*s%s\n", depth, "", entries[i].name); printdir(entries[i].name, depth + 1); } else { printf("%*s - %s\n", depth, "", entries[i].name); } // Free the duplicated name free(entries[i].name); } // Cleanup free(entries); chdir(".."); closedir(dp); } int main(int argc, char* argv[]) { printdir(".", 0); exit(0); }
Key Improvements Explained
- Per-directory sorting: We collect all entries in a directory first, sort them with
qsortusingstrcasecmp(for case-insensitive ordering), then print and recurse. This ensures each directory's contents are sorted properly. - No global variables: We avoid the global
wordarray entirely, which eliminates the overwriting issue and makes the code more modular. - Proper memory management: All allocated strings are freed after use to prevent memory leaks.
- Clear entry tracking: The
DirEntrystruct keeps track of whether each entry is a directory, which is needed for both recursion and tree formatting.
Testing the Updated Code
When you run this, you'll get a tree where every directory's contents are sorted alphabetically without case sensitivity—so Tree.c, tree, and TREE will all be grouped together in the order you'd expect.
内容的提问来源于stack exchange,提问作者Vearaa

