【问题标题】:Infinite loop after reallocrealloc 后无限循环
【发布时间】:2021-03-26 11:19:01
【问题描述】:

这里是一个业余爱好者,很抱歉提出这个问题。

我一直在尝试做 K&R ex。 1-13 但具有动态内存分配和指针。 K&R前。 1-13:“编写一个程序来打印输入中单词长度的直方图。水平条形很容易绘制直方图;垂直方向更具挑战性。”

因为我使用的是Windows,所以我将书中使用的EOF替换为主循环条件下的'q',后者必须与预期的最后一个单词用空格隔开。

在没有执行带有 realloc 的块语句之前,一切都运行良好。因此,如果输入是没有引号的“stackoverflow 是最好的 q”,则条形图会正确显示,因为字数 (4) 可以存储在最初分配的内存中(5 个块)。但是,如果输入包含超过 5 个单词,则结果是无限循环。有人可以解释一下做错了什么吗?

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

static void exit_if_null_ptr(int* x) {
  if (NULL == x) {
    printf("memory allocation error!");
    exit(-1);
  }
}

static void draw_histogram_horizontal(int* length_of_words, int word_count) {
  for(int i = 0; i < word_count; i++) {
    int tmp = *(length_of_words + i);
    while (tmp) {
      printf("*");
      tmp--;
    };
    printf("\n");
  }
}

static void histogram_word_length(void) {
  char c = '\0';
  int word_count = 0, // also offset to length_of_words pointer
     inside_word = 0;
  size_t mem_blocks = 5;
  int* length_of_words = (int*)calloc(mem_blocks, sizeof(int));
  exit_if_null_ptr(length_of_words);
  while ('q' != (c = getchar())) {
    if (' ' == c || '\t' == c || '\n' == c) {
      if (inside_word) {
        inside_word = 0;
        word_count++;
        if (word_count == (int)mem_blocks) {
          mem_blocks += 5;
          length_of_words = (int*)realloc(length_of_words, mem_blocks);
          exit_if_null_ptr(length_of_words);
        }
      }
    }
    else {
      inside_word = 1;
      *(length_of_words + word_count) += 1;
    }
  }
  draw_histogram_horizontal(length_of_words, word_count);
  free(length_of_words);
}

int main(void) {
  histogram_word_length();
  return 0;
}

【问题讨论】:

  • 如果 realloc 失败,length_of_words 将被赋值为 NULL,但原始值没有被释放。
  • realloc 的大小似乎也不正确:增加分配需要很多 bytes,而 calloc 需要很多 blocks指定的大小。混合它们似乎是一个糟糕的主意,你应该在这两种情况下都使用realloc。另一个问题是realloc 没有将其内存归零,因此额外的内存未初始化,从中读取的是 UB。
  • 我无法重现该问题:echo 'stackoverflow is the very best a q'| ./a.out 给了我 6 个小节。请使用触发错误的输入更新问题。
  • realloc 不会将新内存归零。

标签: c


【解决方案1】:

您的代码具有未定义的行为,因为传递给 realloc() 的新大小不正确:length_of_words = (int*)realloc(length_of_words, mem_blocks); 应该是:

length_of_words = (int*)realloc(length_of_words, mem_blocks * sizeof(int));

请注意,使用数组对象的类型更安全,以避免在将其更改为其他对象或复制不同类型的代码时出现问题:

length_of_words = realloc(length_of_words, mem_blocks * sizeof(*length_of_words));

此外,数组的新部分未初始化,您的算法依赖于它被初始化为 0。

size_t new_size = mem_blocks + 5;
length_of_words = realloc(length_of_words, new_size * sizeof(*length_of_words));
exit_if_null_ptr(length_of_words);
while (mem_blocks < new_size)
    length_of_words[mem_blocks++] = 0;

代码中还有其他问题:

  • char c = '\0'; 应该是int c;

  • 您应该测试EOF 以避免文件末尾出现无限循环:

      while ((c = getchar()) != EOF && c != 'q')
    

【讨论】:

  • 代码也有未定义行为,因为新内存未初始化,读取为UB。
  • wrt int c,条件也应该是while ((c = getchar()) != EOF),这样它不会阻止使用q 字符(在这种情况下Ctrl-D 结束程序)。跨度>
  • 您的第二点不正确,length_of_words[word_count] 每个字符递增一次。
  • 非常全面的答案,非常感谢 chqrlie 和 Masklinn!
猜你喜欢
  • 2021-03-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-25
  • 1970-01-01
  • 2018-06-09
  • 2018-03-31
相关资源
最近更新 更多