【问题标题】:Using getline consecutively causes segmentation fault连续使用 getline 会导致分段错误
【发布时间】:2020-10-04 11:54:59
【问题描述】:

我正在尝试使用 getline 从标准输入读取两行连续的输入。这个最小的代码会导致分段错误,我不知道为什么:

int main() {
    char *ln1 = NULL;
    char *ln2 = NULL;
    size_t lnsz1;
    size_t lnsz2;

    getline(&ln1,&lnsz1,stdin);
    getline(&ln2,&lnsz2,stdin);


}

谁能解释我做错了什么?

【问题讨论】:

  • char 指针 ln1/ln2...我还没有检查 getline 返回码 @stark
  • 指针不保存数据。他们指向数据
  • 呃,我想 getline 为数据分配了什么?
  • @stark 来自 Linux 程序员手册关于 getline: "ssize_t getline(char **lineptr, size_t *n, FILE *stream); 如果 *lineptr 设置为 NULL 并且 *n 设置为 0在调用之前,getline() 将分配一个缓冲区来存储该行。”因此,将 ln1ln2 用作字符指针是可以的。错误一定是缺少大小初始化。
  • 我同意将大小设置为零应该可以解决问题。将指针设置为 null 至关重要,应该足够了。但是,将大小设置为零也可以消除我认为是错误实现的任何借口的最后痕迹。如果未初始化变量中的值足够大,则该函数可能会分配大量内存并因此而遇到麻烦。

标签: c pointers getline


【解决方案1】:

我在comment 中说过,将大小设置为零(以及指向空指针的指针)应该可以解决观察到的崩溃问题。

隐藏在后台的是一个假设,即导致崩溃的getline()的具体实现有些错误。它可能出错的一种可能方式是它在分配空间时使用 size 参数,因为指针为空。这有点不太可能,但问题没有提供有关哪个平台导致崩溃的信息。

getline() 的 POSIX 规范说:

ssize_t getline(char **restrict lineptr, size_t *restrict n, FILE *restrict stream);

应用程序应确保*lineptr 是可以传递给free() 函数的有效参数。如果*n 不为零,则应用程序应确保*lineptr 要么指向一个大小至少为*n 字节的对象,要么是一个空指针。

如果*lineptr是空指针或*lineptr指向的对象大小不足,则应像malloc()那样分配对象,或者像realloc()那样重新分配对象,分别地,使得对象足够大以容纳要写入它的字符,包括终止 NUL,并且 *n 应设置为新大小。如果对象已分配,或者如果重新分配操作移动了对象,则应更新*lineptr 以指向新对象或新位置。

在成功完成后,getline()getdelim() 函数应返回写入缓冲区的字节数,如果在 EOF 之前遇到分隔符,则包括分隔符,但不包括终止 NUL 字符。如果设置了流的文件结束指示符,或者没有读取字符并且流处于文件结束位置,则应设置流的文件结束指示符并且函数应返回 - 1.如果发生错误,则应设置流的错误指示符,该函数应返回-1并设置errno以指示错误。

问题中的代码并没有明显滥用这些规则。应用程序确保*lineptr 是一个空指针——这是一个可以传递给free() 的值。 *n 的值是多少并不重要,因为*lineptr 是一个空指针。

一种实验方法是尝试在未初始化的变量(lnsz1lnsz2)中打印值,但这样的实验可以说是调用了未定义的行为。另一种是将变量初始化为一些较大的值——例如SIZE_MAXSIZE_MAX / 2(其中SIZE_MAX 在C11 中定义<stdint.h>,但在早期版本的C 标准中没有)或其他类似的膨胀值。当然,您还应该使用0 的值进行测试,并且可以使用其他较小的值进行一些测试,例如1816 等。所有这些测试都将使用指针进行设置为空指针。如果您可以使用内存分配包来调用malloc()(或realloc())的调试版本,您可能能够记录所请求的内存大小。如果在第二次调用时发生崩溃,您可能会在第一次调用getline() 后打印lnsz1 的值来获取一些信息。

不过,这样的调查几乎不值得。将指针和大小都设置为零应该可以解决问题。如果没有,还有更多需要调查。不过,这不太可能是问题所在。

//#define _XOPEN_SOURCE 700 // Explicitly request POSIX support
#include <stdio.h>

int main(void)
{
    char *ln1 = NULL;
    char *ln2 = NULL;
    size_t lnsz1 = 0;
    size_t lnsz2 = 0;
    ssize_t len1 = -1;
    ssize_t len2 = -1;

    if ((len1 = getline(&ln1, &lnsz1, stdin)) != -1)
    {
        printf("%zd (%zu: %p) [%s]\n", len1, lnsz1, (void *)ln1, ln1);
        if ((len2 = getline(&ln2, &lnsz2, stdin)) != -1)
            printf("%zd (%zu: %p) [%s]\n", len2, lnsz2, (void *)ln2, ln2);
    }
    free(ln1);
    free(ln2);

    return 0;
}

请注意,getline() 在检测到错误或文件结尾时专门返回 -1 而不是 EOF。在大多数系统上,这两个值是相同的,但C standard 只要求EOF 是负数——它并不要求它是-1。我无法确定0 的返回值对getline() 有效的任何情况。

【讨论】:

    猜你喜欢
    • 2018-06-09
    • 1970-01-01
    • 2015-01-18
    • 2021-05-25
    • 1970-01-01
    • 2016-01-06
    • 2013-10-27
    • 1970-01-01
    • 2018-01-18
    相关资源
    最近更新 更多