c - Malloc 和链表的问题

标签 c linked-list malloc

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

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。如果有人能够指出我正确的方向,我将不胜感激。

最佳答案

问题

  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结构中的变化。

关于c - Malloc 和链表的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39990828/

相关文章:

c - 不使用任何算术设计 3 位解决方案。 (提示: use scanf.)

c - argv 问题,c 新手

java - 获取linkedHashMap的key和value

c - 这段代码给出了 head->next = NULL,尽管它不应该

c - 使用循环动态数组(2D)在 C 中 malloc 数组

c - 为什么我的 C 代码不能使用指针和结构体工作?

c - popen2 : reading works, 写入不

c - ANSI C 如何在 Linux 中获取名称服务器 (DNS) 地址?

java - 预定义的java LinkedLists是双向链接的吗?

c - 错误 "assignment makes integer from pointer without a cast array = NULL"