【问题标题】:Trouble with Malloc and Linked ListMalloc 和链表的问题
【发布时间】:2016-10-12 05:00:23
【问题描述】:

我有一个函数可以读取一个文本文件,该文件的每一行都填充了一个单词。这是我正在使用的文本文件的示例

and
but
five
follows
four
has
is
like
line
lines
littlest
not
once
one
only
other
six
the
three
twice
two
word
words

代码:

typedef struct node node_t;

struct node {
    char data[MAX_WORD];
    int term;
    node_t *next;
};

node_t *head; 

int
int_struct(int lines){
    FILE *fp;
    char ch;
    int n = 0, i, switch_num=1, test_first=0, test_first_2=0;
    node_t *node, *curr_add; 
    fp = fopen("text.txt", "r");
    node = (node_t*)malloc(sizeof(node_t));
    for (i=1; i<=lines; i++){
        switch_num = 1;
        n=0;
        if (test_first != 0){
            if (test_first_2){
                node = (node_t*)malloc(1000000);
            }
            test_first_2=1;
            while ((ch = getc(fp)) != '\n'){
                node -> term = i;
                node -> data[n] = ch;
                n++;
            }
            curr_add -> next = node;
            curr_add = node;
        }
        else{
            test_first = 1;
            head = curr_add = node;
        }
    }
    curr_add -> next = NULL;
    fclose(fp);
    return num;
}

我要做的是读取每个单词并将其添加到链表中。
但是我在使用 malloc 时遇到了问题(目前我只是添加了很多字节)并且需要关于如何在我拥有的函数中正确使用它的建议。我已经进行了一般搜索,并尽我所能尝试做大多数示例所做的事情。但我似乎仍然无法让我的功能正常工作。例如,每次我执行程序时,它都会读取所有单词并将其添加到链表中。但是,程序在最后一个单词上崩溃,并返回 NULL。如果有人能指出我正确的方向,我将不胜感激。

【问题讨论】:

  • 需要用水晶球看struct node_t的定义吗?另外,为什么要为每个节点分配 100 万字节?你只需要sizeof(struct node_t)。为 word 分配数据是另一回事。所以请参考我的第一个问题。
  • 您在此函数中使用了尚未为我们定义的全局变量。您需要粘贴问题的完整示例。还有一个最小的问题:即使没有从文件中读取,malloc 是否也有问题?
  • 至于您的崩溃,很可能当getc 返回EOF 时,您会不断循环并在单词中添加字节(因为EOF != '\n'),而实际上您应该退出循环。如果是这种情况,如果您使用了调试器,您应该立即找到它。
  • 你为什么不考虑使用 fgets 让生活更轻松
  • 抱歉,第一次发帖。我刚刚更新了结构定义。我不把整个程序放进去的唯一原因是因为它很大并且做一些其他工作正常的事情。但是,如果您真的需要它,我可以使用整个功能更新帖子。谢谢!

标签: c linked-list malloc


【解决方案1】:

问题

  1. 没有检查返回值。特别是fopenmalloc 可能会返回NULL。如果他们这样做,您将在 第一次尝试访问返回的值。

  2. 逻辑过于复杂。您不需要这些 switch_numtest_firsttest_first_2 变量(参见下面的示例代码)。

  3. 当您逐行读取文本文件时,getc 不需要 - 使用 改为fgets

  4. 内存分配过多。每行不需要超过 sizeof(node_t) + 行长度 个字节。

  5. 分配的内存没有被释放。动态内存应该被释放为 不需要时尽快。

使用链表的示例

下面将一个文本文件读入一个链表。内存分配给 每个列表项,以及文件中的每一行,导致 n * 2 内存 分配,其中n 是文件中的行数。

#include <stdio.h>
#include <stdlib.h>
#include <string.h> /* strerror, strdup */
#include <errno.h>

typedef struct _node {
  unsigned line;
  char *data;
  struct _node *next;
} node_t;


static void
destroy_list(node_t *list)
{
  node_t *node;

  for (node = list; node; node = node->next) {
    if (node->data != NULL)
      free(node->data);
    free(node);
  }
}


static node_t *
create_list_item(const char *data, unsigned line)
{
  node_t *node = calloc(1, sizeof(node_t));

  if (node == NULL) {
    fprintf(stderr, "calloc: %s\n", strerror(errno));
  } else {
    node->line = line;
    node->data = strdup(data);
    if (node->data == NULL) {
      fprintf(stderr, "strdup: %s\n", strerror(errno));
      free(node);
      node = NULL;
    }
  }

  return node;
}


/* Returns pointer to new linked list */
static node_t *
read_file(FILE *fp, char *buf, size_t buf_len)
{
  node_t *list = NULL;
  node_t *prev = NULL;
  node_t *node;
  unsigned i;

  for (i = 0; fgets(buf, buf_len, fp); prev = node) {
    if ((node = create_list_item(buf, ++i)) == NULL) {
      fprintf(stderr, "calloc: %s\n", strerror(errno));
      break;
    }

    if (list == NULL)
      list = node;

    if (prev != NULL)
      prev->next = node;
  }

  return list;
}


static void
print_list(const node_t *list)
{
  const node_t *node;

  for (node = list; node; node = node->next)
    printf("%d: %s", node->line, node->data);
}


int main(int argc, char const* argv[])
{
  const char *filename = "text.txt";
  char buf[1024] = {0};
  FILE *fp = NULL;
  node_t *list = NULL;

  if (NULL == (fp = fopen(filename, "r"))) {
    fprintf(stderr, "failed to open file %s: %s\n",
        filename, strerror(errno));
    return 1;
  }

  list = read_file(fp, buf, sizeof(buf));
  fclose(fp);

  if (list) {
    print_list(list);
    destroy_list(list);
  }

  return 0;
}

使用动态数组的示例

为文件中的每一行(两次)分配内存效率低下, 不仅因为系统调用(mallocrealloc 等)成本高昂, 还因为这些项目是非连续放置的。访问连续 内存区域通常更快。

在下面的代码中,链表被替换为动态数组。我们 一次初始化 10 行的内存。根据需要增加大小。

#include <stdio.h>
#include <stdlib.h>
#include <string.h> /* strerror, strdup */
#include <errno.h>

typedef struct _node {
  size_t line;
  char *data;
} node_t;


static void
destroy_array(node_t *array, size_t size)
{
  size_t i;
  node_t *item;

  for (i = 0; i < size; i++) {
    item = &array[i];
    if (item->data)
      free(item->data);
  }
  free(array);
}


static void
print_array(node_t *array, size_t size)
{
  size_t i;
  node_t *item;

  for (i = 0; i < size; i++) {
    item = &array[i];
    if (item->data) {
      printf("%ld: %s", item->line, item->data);
    }
  }
}

static node_t *
read_file(FILE *fp, char *buf, size_t buf_len,
    const size_t array_step, size_t *array_size)
{
  node_t *item;
  node_t *array = calloc(array_step, sizeof(node_t));
  size_t size = 0;
  if (array == NULL) {
    fprintf(stderr, "calloc:%s\n", strerror(errno));
    return array;
  }

  while (fgets(buf, buf_len, fp)) {
    if (size && size % array_step == 0) {
      array = realloc(array, sizeof(node_t) * (array_step + size));
      if (array == NULL) {
        fprintf(stderr, "realloc:%s\n", strerror(errno));
        break;
      }
    }
    item = &array[size++];

    item->line = size;
    item->data = strdup(buf);
    if (item->data == NULL) {
      fprintf(stderr, "strdup: %s\n", strerror(errno));
      break;
    }
  }

  *array_size = size;

  return array;
}

int main(int argc, char const* argv[])
{
  node_t *array;
  const size_t array_step = 10;
  size_t array_size;
  const char *filename = "text.txt";
  char buf[1024] = {0};
  FILE *fp = NULL;

  if (NULL == (fp = fopen(filename, "r"))) {
    fprintf(stderr, "failed to open file %s: %s\n",
        filename, strerror(errno));
    return 1;
  }

  array = read_file(fp, buf, sizeof(buf), array_step, &array_size);
  fclose(fp);

  if (array) {
    print_array(array, array_size);
    destroy_array(array, array_size);
  }

  return 0;
}

注意node_t 结构的变化。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-12-02
    • 2018-07-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-15
    相关资源
    最近更新 更多