【问题标题】:Do I need to Allocate Memory for a Dirent Structure我是否需要为 Dirent 结构分配内存
【发布时间】:2013-03-26 17:40:11
【问题描述】:
  • 平台:Windows XP Service Pack 3
  • 编译器:Code::Blocks 12.11 版

我目前正在编写一个程序,该程序将使用 POSIX 目录函数递归删除给定目录。但是我遇到了 readdir() 及其对应的 dirent 结构的问题。我在 readdir 的documentation 中读到,多次调用该函数将覆盖该函数返回的结构中保存的数据。所以我认为 readdir() 必须为结构本身分配内存,然后简单地将指针地址重新分配给捕获它的返回值的结构。我测试了这个理论,我是正确的 readdir() 为它的成员 d_name 分配了内存。我遇到的问题是当目录流为空时 readdir 返回一个 NULL 指针,所以我使用带有条件 (dirent_ptr != NULL) 的 while 循环来迭代整个目录。但是因为 readdir() 将处理结构的内存分配,所以我只需声明一个 dirent 结构并让 readdir() 完成它的工作。不幸的是,由于某种原因,不同的结构被初始化为 NULL(或者它可能是我的编译器),所以我的循环永远不会开始,因为它的条件语句最初不是真的。所以我想我的问题是我在这里做错了什么?

这里是重要的变量声明和包含的库。请注意,所有这些变量都是全局声明的。

#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/stat.h>
#include <string.h>

int recursive_delete(const char *path);
int file_delete(const char *path, int file_size);

struct dirent *direntp;
struct stat *statp;

struct switches
{
    int verbose;
    int no_prompt;
    int continue_if_error;
    int files_only;
}; struct switches switches;

我没有解析相对路径,而是简单地 cd 到作为参数给出的路径,然后使用 .和 .. 通配符来移动目录,以便相对路径(d_names)有效。此外,开关结构只包含命令行开关,应该被忽略,我知道以下代码中有错误,但很遗憾我无法修复它们,因为我无法解决上述问题。

int recursive_delete(const char *path)
{
    DIR *dirp;
    int return_value = 0;
    int recursive_return_value = 0;

    if((chdir(path)) == -1)
    {
        perror("ERROR(3)");
        return 1;
    }
    printf("CDED to \"%s\"\n", path);

    dirp = opendir(".");
    if(dirp == NULL)
    {
        perror("ERROR(4)");
        return 1;
    }
    printf("OPENED \"%s\"\n", path);

    while(direntp != NULL)
    {
        direntp = readdir(dirp);
        if( (direntp == NULL) && (errno != 0) )
        {
            perror("ERROR(5)");
            return 1;
        }
        printf("READ \"%s\" FROM \"%s\"\n", direntp->d_name, path);

        if( (strcmp(direntp->d_name, ".")!=0) && (strcmp(direntp->d_name, "..")!=0) )
        {
            if((stat(direntp->d_name, statp)) == -1)
            {
                perror("ERROR(6)");
                return 1;
            }
            printf("STATED \"%s\"\n", direntp->d_name);

            if(S_ISREG(statp->st_mode))
            {
                printf("DELETING \"...\\%s\\%s\"\n", path, direntp->d_name);
                return_value += file_delete(direntp->d_name, statp->st_size);
                if( (!switches.continue_if_error) && (return_value != 0) )
                {
                    break;
                }
            }
            else if(S_ISDIR(statp->st_mode))
            {
                printf("\n\n\nCALLING RECURSIVE DELETE with \"%s\"\n", direntp->d_name);
                recursive_return_value = recursive_delete(direntp->d_name);
                return_value += recursive_return_value;

                if( (!switches.continue_if_error) && (recursive_return_value != 0) )
                {
                    break;
                }

                if( (!switches.files_only) && (recursive_return_value == 0) )
                {
                    if((chdir("..")) == -1)
                    {
                        perror("ERROR(6)");
                        return 1;
                    }
                    printf("CDED BACK TO \"%s\" FROM \"%s\"\n", path, direntp->d_name);

                    if((rmdir(direntp->d_name)) == -1)
                    {
                        perror("ERROR(7)");
                        return 1;
                    }

                    if(switches.verbose)
                    {
                        printf("DELETED DIRECTORY \"...\\%s\\\"\n\n\n", direntp->d_name);
                    }
                }
            }
        }
    }

    return return_value;
}

【问题讨论】:

    标签: c windows directory posix


    【解决方案1】:

    您的代码结构应如下所示(为清楚起见,省略了大多数错误检查):

    int recursive_delete(const char *path)
    {
      DIR* dirp = NULL;
      int return_value = 0;
      char* initial_cur_dir = malloc(1000);
    
      getcwd(initial_cur_dir, 1000);
      chdir(path);
      dirp = opendir(".");
    
      while (dirp != NULL)
      {
        struct dirent* direntp;
        struct stat stat;
    
        direntp = readdir(dirp);
    
        if (direntp == NULL)
          break;
    
        stat(direntp->d_name, &stat);
    
        if (S_ISDIR(statp->st_mode))
        {
          if (strcmp(direntp->d_name, ".") && strcmp(direntp->d_name, ".."))
          {
            return_value += recursive_delete(direntp->d_name);
          }
        }
        else if (S_ISREG(statp->st_mode))
        {
          unlink(direntp->d_name);
        }
      }
    
      if (initial_cur_dir != NULL)
      {
        chdir(initial_cur_dir);
        rmdir(path);
      }
    
    ErrorLabel: // you should goto here when an error is detected
    
      if (dirp != NULL)
      {
        closedir(dirp);
      }
    
      if (initial_cur_dir != NULL)
      {
        chdir(initial_cur_dir);
        free(initial_cur_dir);
      }
    
      return return_value;
    }
    

    【讨论】:

    • 感谢您的回复,但我不知道这对我有何帮助。每次循环开始时声明 direntp 是没有意义的,除非我将 dirent 结构的实际值复制到不同的结构,因为 readdir() 不使用接收它的返回值的 dirent 结构的指针地址。另外我认为您错过了更改当前目录的意义。如果我使用上面的代码,相对路径将有效,rmdir(direntp->d_name) 将返回 No such file or directory 错误,因为 recursive_delete() 将更改 cwd。
    • 最后没看到第二个chdir()???我更喜欢使用尽可能少的全局变量来避免竞争条件之类的事情,并将相关代码和数据保持在一起。
    • 再想一想,上面的代码并没有删除指定的目录,只删除了其中的所有内容。我会更新它来解决这个问题。
    【解决方案2】:

    从附加的代码中,不清楚 direntp 的初始化位置(在while 循环之前)。可能尝试类似:

    direntp = readdir(dirp);
    while(direntp != NULL)
    {
        // all your work here
    
        direntp = readdir(dirp);
    }
    

    此模式可确保 direntp 为您的 while 循环初始化和更新。但是,在您的代码的第二眼中,我并不完全确定 while 循环首先应该做什么。 direntpdirp 在您的循环中如何变化?

    您有可能只使用if 测试(而不是while)并让递归调用处理“循环”效果...

    【讨论】:

    • 感谢您的回复并回答您的问题 direntp 未初始化,它仅在 main 函数之前的空白处全局声明。这是我的问题,我想知道它是如何自己初始化为 NULL 的。此外,direntp 应该保存 dirp 指向的文件/目录的文件/目录名称,它应该是当前工作目录的目录流的当前索引。
    • 哦,我差点忘了,是的,使用 if 语句而不是循环会起作用,但这会使堆栈溢出更有可能,因为将为原始参数的每个文件和子目录调用该函数。 recursive_delete() 被设计成当它在当前工作目录中找到一个子目录时才应该调用它自己。
    • @JohnVulconshinz 关于direntp 初始化:如果您幸运,您的声明将被清零(即NULL),但这通常不能保证。我在不同的平台/环境上看到了不同的行为。如果没有显式分配,您的指针最初可能会有一些垃圾值。
    猜你喜欢
    • 2015-12-07
    • 2017-07-06
    • 2016-05-06
    • 1970-01-01
    • 1970-01-01
    • 2016-01-27
    • 1970-01-01
    • 2011-06-01
    相关资源
    最近更新 更多