我在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 是一个空指针。
一种实验方法是尝试在未初始化的变量(lnsz1 和lnsz2)中打印值,但这样的实验可以说是调用了未定义的行为。另一种是将变量初始化为一些较大的值——例如SIZE_MAX 或SIZE_MAX / 2(其中SIZE_MAX 在C11 中定义<stdint.h>,但在早期版本的C 标准中没有)或其他类似的膨胀值。当然,您还应该使用0 的值进行测试,并且可以使用其他较小的值进行一些测试,例如1、8、16 等。所有这些测试都将使用指针进行设置为空指针。如果您可以使用内存分配包来调用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() 有效的任何情况。