将 getline() 输出保存到外部数组

Saving getline() output to an external array

外部数组 srclns 应保留文本文件中的每个读取行。但是之后阅读它的内容似乎阅读行是空字符串。我在下面的代码中缺少什么?

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

#define MAXSRC 20


char *srclns[MAXSRC];   /* source lines */

size_t read_lines(char *path)
{
  FILE *stream;
  ssize_t read;
  char *lnptr;
  size_t n;
  size_t count;
  stream = fopen(path, "r");
  lnptr = NULL;
  n = 0;
  count = 0;
  if (!stream) {
    fprintf(stderr, "Can't open source '%s'\n", path);
    exit(EXIT_FAILURE);
  }
  while ((read = getline(&lnptr, &n, stream)) != -1) {
    srclns[count++] = lnptr;
  }
  free(lnptr);
  fclose(stream);
  return count;
}


int main()
{

  size_t n = read_lines("foo.txt");
  for (size_t i = 0; i<n; i++)
    printf("%zu %s\n", i, srclns[i]);
  exit(EXIT_SUCCESS);
}

此后仅打印看似空字符串的行号:

0 
1 
2 
3 
4 
5 

函数 getline 仅在 lnptrNULL (ref) 时才分配内存。第一次迭代是这样,但之后需要重新设置为NULL

while ((read = getline(&lnptr, &n, stream)) != -1) {
  srclns[count++] = lnptr;
  lnptr = NULL;
}

否则,lnptr 仍将指向在所有后续迭代的第一次迭代中分配的内存,并且 getline 将反复尝试写入该位置。

即使这不是问题的原因,分配的内存也应该被释放。例如,通过在 exit(EXIT_SUCCESS):

之前添加这些行
for (size_t i = 0; i<n; i++)
  free(srclns[i]);

使用 getline 是否是一个好的做法是另一个您可能想要研究的讨论。它不是最便携的解决方案。

据我所知,您的程序不仅无法运行,而且可能存在内存泄漏。这是由于 getline 使用动态分配的行为。

让我们仔细看看您的程序做了什么,特别是 while ((read = getline(&lnptr, &n, stream)) != -1) 循环:

getline 将与类型为 char**.

&lnptr 一起使用
  • 如果指针是 NULL 它将在堆上分配足够的内存(动态)来存储正在读取的行。
  • 如果指针不是 NULL 那么它应该指向大小为 n 的缓冲区
    • 如果缓冲区足够大(大于或等于行长度),它用于存储字符串。
    • 如果缓冲区太小,那么getline 会重新分配内存,以便有足够大的缓冲区可用。重新分配后,n 会更新为新的缓冲区大小。在某些情况下,重新分配将意味着 lnptr 必须修改并且将会修改。 (如果在当前缓冲区之后没有足够的连续可用内存,则可能会发生这种情况。在这种情况下,内存将分配到堆上的其他地方。如果您对此感兴趣,我建议您进行研究,因为动态内存分配是一个相当复杂的主题,否则只知道指针可能会改变,现在就足够了)。

下面是你的程序的问题(至少这是我可以从我所掌握的信息中推断出来的。我可能是错的,但这似乎是最合理的解释):

  • 在循环的第一次迭代中 lnptrNULL。因此 getline 在堆上分配内存并存储该行,并更新 lnptr 以指向新分配的缓冲区。
  • 在循环中,您将指向已分配缓冲区的指针存储在srclns[0]
  • 在随后的迭代中 缓冲区被覆盖并且可能被 getline 调整大小,您仍然存储指向同一缓冲区的指针 srclns[count].
  • 循环后释放缓冲区并丢弃srclns中每个指针指向的内存。
  • 当你打印时,你很可能读取了一个无效的内存区域(这是你刚刚释放的指针所指向的区域),幸运的是它似乎以终止符开头(您文件的最后一行可能是一个空行,并且在 free 之后没有任何主动更改此内存区域...)

如何解决: 您可以使用 malloc and/or calloc 显式处理动态分配,但这看起来有点复杂,如前所示,getline 可以为您处理。我的建议如下:

  1. srclns 中的所有元素设置为 NULL
    for(int i = 0; i < MAXSRC; ++i)
    {
       srclns[i] = NULL;
    }
    
  2. 然后修改 while 循环以在每次迭代中传递 srclns 的新元素。每次调用 getline 都会看到一个 NULL 指针,从而分配内存并更新 srclns 的单元格以指向它。这个实现的好处是你永远不会超出 srclns 的范围:
    for(int i = 0; i < MAXSRC; ++i)
    {
        n = 0
        if(getline(&srclns[i], &n, stream) == -1)
        {
            break;  // We get out if we reached OEF
        }
    }
    
  3. 在您为 printf 访问 main 后释放所有分配的内存
    for(int i = 0; i < MAXSRC; ++i)
    {
      if(srclns[i] != NULL)
      {
         free(srclns[i]);
      }
    }
    
  4. 调整。我没有对代码进行测试,所以我可能犯了一些错误......请随时纠正它。您可能还想调整代码以满足您的需要。