C 中 Linux 特定 getline() 函数的意外行为

C 中 Linux 特定 getline() 函数的意外行为
#include <stdio.h>
#include <stdlib.h>
#define MAXLEN 1024

void reverse(FILE *, FILE *);

int main(int argc, char ** argv)
{
  ...
  reverse(fptr, stdout);
  ...

  return 0;
}

void reverse(FILE * instream, FILE * outstream)
{
  char ** buf;
  char * lbuf;
  int counter, i;
  size_t slen;

  counter = 0;

  buf = malloc(MAXLEN * sizeof(char));
  if (buf == NULL)
  {
    fputs("malloc failed\n", stderr);
    exit(EXIT_FAILURE);
  }

  lbuf = NULL;
  while ((counter < MAXLEN) && (getline(&lbuf, &slen, instream) != -1))
  {
    buf[counter] = lbuf;
    counter++;
    lbuf = NULL;
  }


  for (i = counter - 1; i >= 0; i--)
  {
    fputs(buf[i], outstream);
    free(buf[i]);
  }

  free(buf);
}

我编写了这个函数来以相反的顺序打印文件,例如

Hello World
How are you ?
what are you doing ?

输出应该是这样的

what are you doing ?
How are you ?
Hello World

但出现了意想不到的行为。我正在使用 Linux 特定stdio功能getline()来扫描任意长行。问题是,如果 的值为MAXLEN4, 5, 6, ... 这样,程序在打印输出时会给出double free or corrupt address error abort()特定的功能。free()

我查看了该函数返回的地址,geline()似乎在 4 次迭代之后,该函数返回的第一个缓冲区的地址getline()已损坏。如果MAXLEN是 2 或足够大(例如 1024),则不会出现问题。

输入样本我拿的

When to the sessions of sweet silent thought
I summon up remembrance  of things past,
I sigh the lack of many a thing I sought,
And with old woes new wail my dear time's waste:
Then can I drown an eye, unused to flow,
For precious friends hid in death's dateless night
And weep afresh love's long since cancell's woe,

答案1

在 中char ** buf;, 的类型bufpointer to char*,您在其中为 的数组分配空间char*

buf = malloc(MAXLEN * sizeof(char));

这仅为 MAXLEN 字符分配空间,而不为 MAXLEN 指针分配空间(每个指针在 64 位 CPU 上为 8 个字节)。因此,您稍后会超出分配的空间,并且条目会损坏(它们与 重叠buf[counter])。

正确的分配是:buf = malloc (MAXLEN * sizeof(char *));

通常我更愿意根据返回的指针的类型进行分配:

buf = malloc (MAXLEN * sizeof(*buf));

即“为事物buf指向的 MAXLEN 项分配空间”。这可以避免buf更改类型时引入的错误。

getline()不是问题的一部分:您只需将它返回的指针存储在一个对它们来说太小的数组中。您的代码适用于小数据集的原因是,将malloc()分配四舍五入到 16 或 32 字节以保持对象的对齐(两者都是针对其自己的空闲列表,因此编译器可以对用户结构的对齐做出合理的假设)。

因此(通常)您的前两行数据正常,接下来的两行会损坏下一行的隐藏标题(破坏free()),之后的行会损坏您的文本数据。

相关内容