调用函数并将另一个结构作为参数传递后,结构的元素将失去其值

An element of a struct loses its value after a function is called passing another struct as argument

我有一个问题想不通。我有以下文件:file_reader.c, file_reader.h, file_writer.c, file_writer.h, test_file_reader.c

我正在使用 'struct' 读写文件。为了更好地理解我写了下面的代码 test_file_reader.c:

#include <stdio.h>
#include "file_reader.h"
#include "file_writer.h"

int main ()
{   
    char *file_path = "/home/freitas/Dropbox/projects/gcleaner/cleaners/custom.xml";
    struct FileReader *fr = malloc(sizeof(struct FileReader));

    file_reader_new (file_path, fr);    

    show_file_reader_values(fr);

    struct FileWriter *fw = malloc(sizeof(struct FileWriter));

    fw->file_path = "/tmp/text1.txt";
    fw->content = "aaa";
    write (fw);

    show_file_reader_values(fr);

    return 0;
}

void show_file_reader_values(const struct FileReader *fr)
{
    printf("==========FILE READER==========\n");    
    printf("file path: %s\n", fr->file_path);
    printf("----------file content---------\n");    
    printf("content:\n%s\n", fr->content);
    printf("----------file content---------\n");
    printf("n lines: %d\n", fr->n_lines);
    printf("n characters: %d\n", fr->n_characters); 
    printf("==========FILE READER==========\n\n");
}

函数'file_reader_new'读取文件,然后将内容、文件路径、行数和字符数签名到'struct' 'FileReader'.

当我第一次调用函数'show_file_reader_values'时,内容没有问题,但是当我调用函数'write'然后再次调用函数'show_file_reader_values'时,内容不一样了。问题是文件 'file_writer.c' 及其结构的函数 'write' 与文件 'file_reader' 及其结构没有任何关系。那么,使用另一个结构的函数如何更改另一个文件的另一个结构的值?

输出:

[freitas@localhost test]$ ./test_file_reader 
==========FILE READER==========
file path: /home/freitas/Dropbox/projects/gcleaner/cleaners/custom.xml
----------file content---------
content:
<cleaner>  <id>k3b</id>  <label>k3b</label>  <description>Disc writing software</description>  <option>  <id>log</id>  <label>Log</label>  <description>Delete the log file which contains information about the last writing session(s).</description>  <command>delete</command>  <search>glob</search>  <path>~/.kde/share/apps/k3b/*.log</path>  </option>  <option>  <id>log2</id>  <label>Log</label>  <description>Delete the log file which contains information about the last writing session(s).</description>  <command>delete</command>  <search>glob</search>  <path>~/.kde/share/apps/k3b/*.log</path>  </option>  </cleaner>

----------file content---------
n lines: 1
n characters: 621
==========FILE READER==========

==========FILE READER==========
file path: /home/freitas/Dropbox/projects/gcleaner/cleaners/custom.xml
----------file content---------
content:
<cleaner>  <id>k��U�N
----------file content---------
n lines: 1
n characters: 621
==========FILE READER==========

你看到了吗?在第一次通话中,我得到了完整的输出:

<cleaner>  <id>k3b</id>  <label>k3b</label>  <description>Disc wri...

但在第二次通话中我接到了:

<cleaner>  <id>k��U�N

file_reader.c

#include <stdio.h>
#include <stdlib.h>
#include "file_reader.h"

int file_reader_new(const char *file_path, struct FileReader *fr)
{
  char    *content;      // holds the file content.
  int     counter;           // holds the file number of lines.
  size_t  i;             // indexing into content.
  size_t  buffer_size;   // size of the content.
  char    *temp;         // for realloc().
  char    c;             // for reading from the input.
  FILE    *input;        // our input stream.

  if ((input = fopen(file_path, "r")) == NULL) {
    fprintf(stderr, "Error opening input file %s\n", file_path);
    exit(EXIT_FAILURE);
  }

  /* Initial allocation of content */
  counter = 0;
  i = 0;  
  buffer_size = BUFSIZ;
  if ((content = malloc(buffer_size)) == NULL) {
    fprintf(stderr, "Error allocating memory (before reading file).\n");
    fclose(input);
  }

  while ((c = fgetc(input)) != EOF) {

    /* Enlarge content if necessary. */
    if (i == buffer_size) {
      buffer_size += BUFSIZ;
      if ((temp = realloc(content, buffer_size)) == NULL) {
        fprintf(stderr, "Ran out of core while reading file.\n");
        fclose(input);
        free(content);
        exit(EXIT_FAILURE);
      }
      content = temp;
    }

    /* Add input char to the content. */
    content[i++] = c;

    /* If the character is a break of line
     * then the counter will be incremented.
     */
    if (c == '\n')
      counter++;
  }

  /* Test if loop terminated from error. */
  if (ferror(input)) {
    fprintf(stderr, "There was a file input error.\n");
    free(content);
    fclose(input);
    exit(EXIT_FAILURE);
  }

  /* Make the content a bona-fide string. */
  if (i == buffer_size) {
    buffer_size += 1;
    if ((temp = realloc(content, buffer_size)) == NULL) {
      fprintf(stderr, "Ran out of core (and only needed one more byte too ;_;).\n");
      fclose(input);
      free(content);
      exit(EXIT_FAILURE);
    }
    content = temp;
  }
  content[i] = '[=14=]';

  /* Assigns the variables to the corresponding
   * element of the struct.
   */
  fr->file_path = file_path;
  fr->content = content;
  fr->n_lines = counter;
  fr->n_characters = i;

  /* Clean up. */
  free(content);
  fclose(input);

  return 0;
}

file_reader.h

#ifndef FILE_READER_H_
#define FILE_READER_H_

typedef struct FileReader
{
    char *content;           // holds the file content.
    char *file_path;     // holds the file path.
    int  *n_lines;           // holds the number of lines.
    int  *n_characters;  // holds the number of characters.
} FileReader;

// file_reader_new - reads the file
int file_reader_new(const char *file_path, struct FileReader *fr);

#endif

file_writer.c

#include <stdio.h>
#include "file_writer.h"

void write (struct FileWriter *fw)
{
  FILE *f = fopen(fw->file_path, "w");
  if (f == NULL)
  {
      printf("Error opening file!\n");
      exit(1);
  }

  fprintf(f, "%s", fw->content);   

  fclose(f);
}

file_writer.h

#ifndef FILE_WRITER_H_
#define FILE_WRITER_H_

typedef struct FileWriter
{
    char *file_path;
    char *content;
    int  *error;
} FileWriter;

#endif

你能帮帮我吗?谢谢!

struct FileReader *fr = malloc(sizeof(struct FileReader)); 

没有必要这样做。你只需要这个:

struct FileReader fr; 

这里也一样:

struct FileWriter fw; 

然后只需将这些变量的地址传递给必要的函数即可。


请注意,这不是作为答案提供给您的,只是作为注释来稍微清理您的代码以删除对堆的无关调用。碰巧真正的问题存在于其他地方,而您在这里看到的是 undefined behavior 的辉煌。

我不确定您是如何逐个字符或逐块读取文件的,但是无论如何,

因为你更新了content缓冲区中的读取数据,并将content缓冲区中的地址存储在file_reader_new()中到变量fr->content中并立即释放内存将结束失去你阅读的数据。并导致称为 悬挂指针

的情况

悬挂指针

(一个指针变量,指向释放的内存)

这就是为什么总是建议在释放后将指针变量设置为NULL。在某些情况下,取消引用悬空指针将导致 Segmentation faultundefined behavior

此外,由于 struct 的所有成员变量都是指针,因此最好将它们初始化为 NULL

您可以使用 calloc 来初始化 struct 中的所有变量,而不是 malloc 来将所有成员初始化为 NULL,如果您要使用动态分配。这也适用于 string

这是我看到的一个问题:

fr->content = content;
fr->n_lines = counter;
fr->n_characters = i;

/* Clean up. */
free(content);   /* <-- Danger */

您在 file_reader_new 函数中执行此操作。然后调用 show_file_reader_values 并在该函数中访问 content:

printf("content:\n%s\n", fr->content);

由于您对内容调用了 free(),该指针不再指向有效内存,因此发生了未定义的行为。

解决方法是在fr上为内容分配space并将content的字符复制到这个space,或者干脆不调用freecontent.

所以要么这样做:

fr->content = malloc(i + 1);
strcpy(fr->content, content);
fr->n_lines = counter;
fr->n_characters = i;

/* Clean up. */
free(content);   

或者这个:

fr->content = content;
fr->n_lines = counter;
fr->n_characters = i;
/* No call to free(content) done */