fprintf 在 while 循环中无法正常工作

问题描述 投票:0回答:1

我一直在学习 C 中的动态内存分配并在代码中进行练习。虽然我认为我已经正确分配了内存,但我可能是错的。因此,任何有用的评论都可以(但请解释任何建议,以便我能够理解)。

我编写了一个 C 程序,首先获取当前工作目录中的文件列表,然后写入每个文件。但是循环在完成所有文件的获取之前就看到了 NULL。在此之前就出现了段错误,但我设法修复了它。它没有指向内存的正确部分,这导致像

fopen()
这样的函数无法获取任何文件名。

但是,虽然我可以打印出文件列表,但

writeToFiles()
中的循环并没有完成每当我使用几乎任何
f*
(文件相关)C 库函数时动态分配的文件名列表。

我已经设法修复了我收到的所有编译器错误和警告消息,但我真的不知道现在发生了什么。我认为正在发生的事情是指向名为

filename
的结构成员的指针不在指针数组的开头。我很确定我的
buildFileList()
功能很好并且工作正常。那么我需要“倒带”指针什么的吗?

如果我可以礼貌地要求那些希望做出回应的人在给出的任何答案中尽可能清晰和完整。因为我显然在这里遗漏了一些东西,但我不知道是什么。

警告:以下代码如果运行将覆盖当前工作目录的内容。

我在下面发布了我的代码:

___dyn_char_mem_alloc.c

char *___dyn_char_mem_alloc(size_t buffSize) {    
    
    // allocate char memory with malloc() for char *        
   
    char *ptr = malloc(buffSize+1);
   
    //printf("buffSize (char): %d\n", buffSize);
    
    // if malloc returns a pointer that is not NULL, we return it to the caller
    if (ptr != NULL) {
        // puts("malloc() pointer returned (char *)\n");
        // printf("ptr in ___dyn_char_mem_alloc() function = %p\n");
        return ptr;
    } else {       
        // puts("malloc() call failed!\n");
        return NULL; 
   }
}

___dyn_char_mem_alloc.h

#include <stdio.h>
#include <stdlib.h> // for malloc()

char *___dyn_char_mem_alloc(size_t buffSize);

___dyn_struct_mem_alloc.c

#include "___dyn_struct_mem_alloc.h"

struct filepath *___dyn_struct_mem_alloc(size_t buffSize) {
        
    // allocate char memory with malloc() for struct
    
    struct filepath *t_filepath = malloc(buffSize);   
    
    // if malloc returns a pointer that is not NULL, we return it to the caller
    if (t_filepath != NULL) {      
        return t_filepath;
    } else {       
        // puts("malloc() call failed!\n");
        return NULL;
    }
}

___dyn_struct_mem_alloc.h

#include <stdio.h>
#include <stdlib.h> // for malloc()
#include <linux/limits.h>

//struct filepath *___dyn_struct_mem_alloc(size_t buffSize);

struct filepath {
    char *path[PATH_MAX];
    char *filename[FILENAME_MAX];
};

build_file_list.c

struct filepath *buildFileList(void) {
    
    DIR *d = opendir(".");
    struct dirent *dir;
    struct stat stats;
    //struct filepath *t_filepath = malloc(sizeof(struct filepath));
    struct filepath *t_filepath = ___dyn_struct_mem_alloc(sizeof(struct filepath)); 
    int i = 0, strLength = 0, totalNumFiles = 0;        
    const char *pDirFsPtr = "..", *curWorkDirFsPtr = ".";        
    
    while (((dir = readdir(d)) != NULL)) {
        
        if ( (!strcmp(dir->d_name, pDirFsPtr)) || (!strcmp(dir->d_name, curWorkDirFsPtr)) )  {              
            continue;
        } else {              
            // check if it's a directory and if so skip it              
            stat(dir->d_name, &stats);
            if (!S_ISDIR(stats.st_mode)) {
                // get length of filename
                strLength = strlen(dir->d_name);
                  
                // allocate some memory for the filename               
                //t_filepath->filename[i] = malloc(sizeof(char) * (strLength+1));
                t_filepath->filename[i] = ___dyn_char_mem_alloc(strLength+1);                
                  
                // let's copy each filename from the dirent structure's d_name member to our newly dynamically allocated memory                 
                //  t_filepath->filename[i]=(dir->d_name); // doesn't matter if i use strcpy() below instead or not - same problem
                strcpy(t_filepath->filename[i], dir->d_name);                  
                //  printf("filepath->filename[%d] name: %s | ptr: %p \n",i, t_filepath->filename[i], t_filepath->filename[i]); // works no problem
                  
                totalNumFiles++;                  
            }                                         
        }
        ++i;
    }    
    closedir(d);
    
    printf("t_filepath struct ptr from buildFileList(): %p\n",  t_filepath);
    printf("t_filepath->filename struct ptr from buildFileList(): %p\n",  t_filepath->filename);
        
    #ifdef DEBUG    
    printf("\nTotal number of files in dir: %d\n\n", totalNumFiles);
    #endif
    
    #ifdef DEBUG
    int k = 0;
    while (t_filepath->filename[k] != NULL) {
        printf("t_filepath->filename[%d] name: %s | ptr addr: %p \n",k, t_filepath->filename[k], t_filepath->filename[k]);
        ++k;
    }
    #endif
    
    return t_filepath;
}

build_file_list.h

#include <stdio.h>
#include <dirent.h>
#include <sys/stat.h>
#include <string.h>
#include <unistd.h>
#include <libgen.h>
#include <linux/limits.h>

struct filepath *buildFileList(void);
char *___dyn_char_mem_alloc(size_t buffSize);
struct filepath *___dyn_struct_mem_alloc(size_t buffSize);

struct filepath {
    char *path[PATH_MAX];
    char *filename[FILENAME_MAX];
};

main.c

#include "main.h"

int main(int argc, char *argv[]) {
        
    //TODO: add a check for command-line args    
       
    someOtherFunction();
        
    return 0;
}

main.h

#include <stdio.h>

int someOtherFunction(void);

some_other_function.c

#include "some_other_function.h"

int someOtherFunction(void) {
    
    struct filepath *t_filenames = NULL;
    int i = 0;
    
    t_filenames = buildFileList();
    
    puts("\n");
    printf("t_filenames struct ptr from someOtherFunction(): %p\n",  t_filenames);
    printf("t_filenames->filename struct ptr from someOtherFunction(): %p\n",  t_filenames->filename);
    puts("\n");
    
    /* while (t_filenames->filename[i] != NULL) {
        printf("t_filenames->filename[%d] name (from someOtherFunction()): %s | ptr addr: %p \n",i, t_filenames->filename[i], t_filenames->filename[i]);
        ++i;
    } */ // works
    
    writeToFiles(t_filenames);
    
    return 0;
}

some_other_function.h

#include <stdio.h>
#include <linux/limits.h>

int someOtherFunction(void);
struct filepath *buildFileList(void);
void writeToFiles(struct filepath *t_filenames);

struct filepath {
    char *path[PATH_MAX];
    char *filename[FILENAME_MAX];
};

write_to_files.c

#include "write_to_files.h"

void writeToFiles(struct filepath *t_filenames) {
    
    FILE *curFile;
    int i = 0;  
    char msg[100] = "giggle, giggle, giggle....";  
    
    puts("\n");
    printf("t_filenames struct ptr from writeToFiles(): %p\n",  t_filenames);
    printf("t_filenames->filename struct ptr from writeToFiles(): %p\n",  t_filenames->filename);
    puts("\n");
    
    while (t_filenames->filename[i] != NULL) { 
        printf("filename name (from writeToFiles()): %s ptr addr: %p\n", t_filenames->filename[i], t_filenames->filename[i]);
        curFile = fopen(t_filenames->filename[i], "wb"); // works, but calling anymore functions were causing segfaults
        if (curFile != NULL) {
            puts("fprintf(curFile, %s, msg);");
            printf("current filename name: %s\n", t_filenames->filename[i]);
            // print childish message to file :-P
            fprintf(curFile, "%s", msg);
            puts("fclose(curFile);"); 
            fclose(curFile);
        } else {
            puts("loop broken!\n"); // always gets here way too early
            break;
        }         
        ++i;        
    }
    
    return;
}

write_to_files.h

#include <stdio.h>
#include <string.h>
#include <stdlib.h> // needed for exit() call -- remove?
#include <linux/limits.h>

struct filepath {
    char *path[PATH_MAX];
    char *filename[FILENAME_MAX];
};

感谢您对此的任何帮助!

c linux malloc fopen fwrite
1个回答
1
投票

几个问题立即显而易见:

  1. 您没有初始化变量,而是依赖它们的值
  2. 您无法确保添加到列表中的文件实际上适合您分配的内存
  3. 您无法确保一旦创建了列表,就有一种有保证的方法可以知道在阅读它时何时停止迭代它。

这并不是代码所有可能问题的完整列表。很可能还有其他问题。

您的

___dyn_struct_mem_alloc()
函数无法初始化结构的内容:

   struct filepath * ___dyn_struct_mem_alloc(size_t buffSize) {
            
       // allocate char memory with malloc() for struct
        
        struct filepath *t_filepath = malloc(buffSize);   
        
       // if malloc returns a pointer that is not NULL, we return it to the caller
       if (t_filepath != NULL) {      
           return t_filepath;
       }
       else {       
           // puts("malloc() call failed!\n");
            return NULL;
       }
    }

但是这段代码:

while(t_filenames->filename[i] != NULL) { 
    .
    .
    .       
}

假设未设置的值为

NULL

您也永远无法确保不会尝试在任何

FILENAME_MAX
中添加超过
struct filepath
个文件。无论有多少个,此外观都会不断向字段添加文件名:

while (((dir = readdir(d)) != NULL)) {
      if ( (!strcmp(dir->d_name, pDirFsPtr)) || (!strcmp(dir->d_name, curWorkDirFsPtr)) )  {              
          continue;
      }          
      else {              
          // check if it's a directory and if so skip it              
          stat(dir->d_name, &stats);
          if (!S_ISDIR(stats.st_mode)) {
              // get length of filename
              strLength = strlen(dir->d_name);
              
              // allocate some memory for the filename               
              //t_filepath->filename[i] = malloc(sizeof(char) * (strLength+1));
              t_filepath->filename[i] = ___dyn_char_mem_alloc(strLength+1);                
              
              // let's copy each filename from the dirent structure's d_name member to our newly dynamically allocated memory                 
            //  t_filepath->filename[i]=(dir->d_name); // doesn't matter if i use strcpy() below instead or not - same problem
              strcpy(t_filepath->filename[i], dir->d_name);                  
            //  printf("filepath->filename[%d] name: %s | ptr: %p \n",i, t_filepath->filename[i], t_filepath->filename[i]); // works no problem
              
              totalNumFiles++;                  
          }                                         
      }
      ++i;
}    
closedir(d);

如果任何目录中的文件数量超过

FILENAME_MAX
,您的代码会溢出缓冲区,从而损坏内存。

如果目录中正好有

FILENAME_MAX
文件,您的代码将无法知道它何时到达列表末尾,因为即使您修复代码并初始化,也不可能有
NULL
哨兵值结构体中的
filename
数组到所有
NULL
指针值。

此外,诸如

___dyn_char_mem_alloc
之类的名称是 保留标识符:

所有以下划线开头的标识符以及大写字母或其他下划线始终保留以供任何使用。

由于您似乎使用的是 Linux,只需删除

___dyn_char_mem_alloc()
以及通过一次调用
strdup()
所做的字符串的手动副本即可。删除诸如此类的无关代码将使其余代码更易于遵循和理解。

© www.soinside.com 2019 - 2024. All rights reserved.