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

标签 c getline

外部数组 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 的行为造成的它使用动态分配。

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

getline 将与 &lnptr 一起使用,其类型为 char**

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

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

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

修复方法: 您可以使用 malloc 和/或 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. 调整。我没有对代码进行测试,所以我可能犯了一些错误......请随时纠正它。您可能还想调整代码以满足您的需求。

关于c - 将 getline() 输出保存到外部数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68996815/

相关文章:

c++停止在ctrl-d上要求输入

awk - 如何在 AWK 中将 getline 与连接在一起的命令一起使用

c - valgrind 使用 getline 函数报告内存丢失

c - 将字符串拆分为一定大小的 char 数组

c++ - 不知道出了什么问题(字符串输入)

c++ - 使用 getline 和 istringstream 从 CSV 文件中读取数据

我可以使用动态链接嵌入到我的 C/C++ 项目吗?

c - 返回动态分配字符串的函数工作一次,然后在后续调用中返回垃圾值(在调用 free 后)

c - rc.local 没有在树莓派启动时运行

c++ - getline() 不打开文本文件