c-在递归存储目录/文件名的过程中,Realloc会占用字符串数组



我在C程序中使用opendir/readdir(cygwin上的GNU)将一些嵌套目录中的文件名收集到字符串数组中(该程序主要使用C89和早期的约定)。由于我事先不知道文件的数量,我决定使用malloc/realloc来执行动态内存分配。指针数组通过递归调用来收集文件名。问题是在早期调用getlist()期间存储的文件名在以后的存储步骤中被损坏。在进入子目录,执行对realloc的第二次调用并从子目录中出现后,在执行realloc之后的存储边缘的字符串会随着父目录中收集到额外的文件名而逐渐损坏。

如果我使用单个malloc分配来创建一个大的初始指针数组来分配内存,我可以避免这个问题,但我希望能够使用realloc。有人能指出我做错了什么吗,特别是我想这就是我在这种情况下使用realloc的方式吗?

#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <stdio.h>
#include <stddef.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
// function prototypes
char ** getlist(char ** filelist,long int *numfiles,char *dirname);
extern int stat (const char *filename, struct stat *buf);   
extern DIR * opendir (const char *_dirname);
extern  struct dirent * readdir (DIR *dirstream);
int main(int narg, char* argv[])
{ 
    // vars
    char * dirname;
    char **filelist, **traverse; 
    long int numfiles =0;
    FILE* pfile;
    //....................................
    // and go ...
    if ( (filelist = (char **)malloc(sizeof(char *)))  == NULL )
    { 
            printf("Fatal malloc error!n"); 
            exit(3);
    }
    filelist = getlist(filelist,&numfiles,argv[1]); // argv[1] is dir name 
    // list the stored filenames and write to file
    pfile = fopen("listoutput.txt","w");
    printf("Stored filenames (N=%i):n",numfiles);
    traverse = filelist;    
    while(*traverse)
    {
        printf("%sn",*traverse);
        fprintf(pfile,"%sn",*traverse);
        traverse++;
    }
    fclose(pfile);
    // free etc should go here...
    return 0 ;
}
//-------------------------------------------------------------------------------
char ** getlist(char** filelist, long int *numfiles, char* dirname)
{
    // variables
    char filename[200];
    char dirname_[200];
    DIR * directory;
    struct dirent * file;
    strcpy(dirname_,dirname);
    // for checking file type
    // macro:   int S_ISREG (mode_t m)  
    struct stat* filestat = malloc(sizeof(struct stat));
    int sizeofchar = sizeof(char); // fields: mode_t st_mode, unsigned char d_namlen
    char **traverse, **ptemp; 
    //aux
    long int ii, icheck;    
    // check number of valid files in dirname and allocate memory in char pointer array
    ii=0; 
    directory = opendir (dirname_); 
    while(file = readdir(directory))
    {
        sprintf(filename,"%s/%s",_dirname_,file->d_name);
        icheck = stat(filename,filestat);
        if (icheck==0)
        {
            if (S_ISREG(filestat->st_mode)) ii++;
        }
        else
        {
            printf("Couldn't check file type of file "%s"  (icheck = %i)n", filename, icheck);
        }
    }
    // generate enough room for all the filename strings
    if ( (filelist=(char **)realloc(filelist,sizeof(char *)*(*numfiles+ii+1)))  ==  NULL )
    {
        printf("Fatal realloc error!n");
        exit(3);
    }
    traverse = filelist + *numfiles; 
    // now store the filenames in filelist ...
    (void) rewinddir (directory);   
    while(file = readdir(directory))
    {
        sprintf(filename,"%s/%s",dirname_,file->d_name);
        icheck = stat(filename,filestat);
        if (icheck==0 && S_ISREG(filestat->st_mode))
        {
            *traverse = (char *)malloc(sizeofchar*(strlen(filename)+1));
            strcpy(*traverse,filename);
            traverse++;
            (*numfiles)++;
            // spit out what we have so far
            printf("nCurrent list (looping):n-----------n");
            ptemp = filelist;   
            ii=*numfiles;
            while(ii--)
            {
                printf("%sn",*ptemp);
                ptemp++;
            }
            printf("n-----------n");          
        //sleep(1); 
        }
        else if (icheck==0 && S_ISDIR(filestat->st_mode))
        {
            if (strcmp(file->d_name,".")!=0 && strcmp(file->d_name,"..")!=0 )
            {
                printf("Processing folder %sn", filename);
                filelist = getlist(filelist,numfiles,filename);
                traverse = filelist + *numfiles; 
                // spit out what we have so far
                printf("nCurrent list (returned from getlist):n-----------n");
                ptemp = filelist;   
                while(*ptemp)
                {
                    printf("%sn",*ptemp);
                    ptemp++;
                }
                printf("n-----------n");
            }       
        }
    }
    (void) closedir (directory);
    *traverse = NULL;
    return filelist;    
}

分配的内存不足。

由于没有为终止空字符分配足够的空间,然后执行strcpy(),内存可能会损坏。

// *traverse = (char *)malloc(sizeofchar*strlen(filename));
// strcpy(*traverse,filename);
size_t size = strlen(filename) + 1;  // Add 1!
*traverse = malloc(size);
memecpy(*traverse, filename, size);

次要:无需从malloc()投射返回。一旦代码知道大小,就使用memcpy()strcpy()

--

更新

与其打印ii次,不如打印到*temp == NULL

 while (ii--) {
    if (*ptemp == NULL) break;
    printf("%sn", *ptemp);
    ptemp++;
  }

ii基于目录大小,但文件名的使用不包括"."one_answers".."。

可疑代码除了GTG之外还有其他问题。

这是AFAIK的一个有效解决方案。事实证明,我没有正确跟踪已经分配的指针数组元素的数量,因此在对realloc的顺序调用过程中,我实际上用新数据覆盖了旧的内存位置。令人惊讶的是,这个错误的结果并没有更戏剧性——我想这是偶然的。

我的解决方案是传递第二个int,它跟踪要存储的字符串(文件名)的数量。我可以在每个递归级别交替扫描指针数组,以动态确定这个数字。

最后,我想知道把它作为一个链表来执行是否会更好。下次再说吧。

#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <stdio.h>
#include <stddef.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
typedef struct stat STAT;

// prototypes
extern int stat (const char *filename, STAT *buf);  
extern DIR * opendir (const char *dirname);
extern struct dirent * readdir (DIR *dirstream);

char ** getlist(char ** filelist,long int *pnumfiles, long int *pnallocated,char *dirname);

int main(int narg, char* argv[])
{ 
    // vars
    FILE* pfile;
    char **filelist, **traverse; 
    long int numfiles, nallocated;
    //....................................
    // and go ...
    if ( (filelist = (char **)malloc(sizeof(char *)))  == NULL )
        { printf("Fatal malloc error!n"); exit(1);}
    nallocated = 1;
    numfiles = 0;
    filelist = getlist(filelist,&numfiles,&nallocated,argv[1]);         // argv[1] should be a directory name
    // list the stored filenames and store to file
    pfile = fopen("listoutput.txt","w");
    if ( pfile  == NULL )
        { fprintf(stderr, "Could not open file for output!"); exit(4);};
    printf("Stored filenames (N=%i):n",numfiles);
    traverse = filelist;    
    while(*traverse)
    {
        printf("%sn",*traverse);
        fprintf(pfile,"%sn",*traverse);
        traverse++;
    }
    printf("nDONE!n");
    fclose(pfile);
    return 0;
}
//-------------------------------------------------------------------------------
char ** getlist(char** filelist, long int *pnumfiles, long int *pnallocated, char* dirname)
{
    // variables
    char filename[200];
    char dirname_[200];
    DIR * directory;
    struct dirent * file;
    // for checking file type
    // macro:   int S_ISREG (mode_t m)  
    STAT* filestat; // fields: mode_t st_mode, unsigned char d_namlen
    long int ifile = 0; // number of files to store from current dir
    int sizeofchar = sizeof(char); // = 1
    //aux
    long int icheck;    
    char **traverse, **ptemp; 
    // and go ....
    // copy string to const char array because opendir demands one
    strcpy(dirname_,dirname);
    if ( (filestat = malloc(sizeof(STAT)) ) == NULL) 
        {
            printf("Error allocating memory for filestat!"); 
            exit(2);
        }
    // check number of valid files in dirname and allocate memory in char pointer array
    directory = opendir (dirname_); 
    while(file = readdir(directory))
    {
        sprintf(filename,"%s/%s",dirname_,file->d_name);
        icheck = stat(filename,filestat);
        if (icheck==0)
        {
            if (S_ISREG(filestat->st_mode)) ifile++;
        }
        else
        {
            printf("Couldn't check file type of file "%s"  (icheck = %i)n", filename, icheck);
        }
    }
    // generate enough room for all the filename strings
    *pnallocated = *pnallocated + ifile;
    if ( (filelist=(char **)realloc(filelist,sizeof(char *)*(*pnallocated)))  ==  NULL )
        {
            printf("Fatal realloc error!n");
            exit(2);
        }
    traverse = filelist + *pnumfiles; 
    // now store the filenames in filelist ...
    (void) rewinddir (directory);   
    while(file = readdir(directory))
    {
        sprintf(filename,"%s/%s",dirname_,file->d_name);
        icheck = stat(filename,filestat);
        if (icheck==0 && S_ISREG(filestat->st_mode))
        {
            size_t size = strlen(filename) + 1;  // Add 1!
            if ( (*traverse = malloc(size)) == NULL ) 
            {
                printf("Fatal malloc error!n");
                exit(3);
            }
            memcpy(*traverse, filename, size);  
            traverse++;
            (*pnumfiles)++;
        }
        else if (icheck==0 && S_ISDIR(filestat->st_mode))
        {
            if (strcmp(file->d_name,".")!=0 && strcmp(file->d_name,"..")!=0 )
            {
                printf("Processing folder %sn", filename);
                filelist = getlist(filelist,pnumfiles,pnallocated,filename);
                traverse = filelist + *pnumfiles; 
            }       
        }
    }
    *traverse = NULL;
    (void) closedir (directory);    
    return filelist;    
}

相关内容

  • 没有找到相关文章

最新更新