openlang

getline hands you the buffer even when it fails

POSIX getline allocates or grows the buffer through the pointer you pass, and the buffer belongs to the caller from the first call onwards. That includes the call that returns -1. The specification leaves the freeing to the application, and the Linux man page states it directly, the buffer should be freed even if getline failed.

So the loop has a single release, after it ends, whatever the reason it ended.

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

int main(void)
{
	char *line = NULL;
	size_t cap = 0;
	ssize_t n;
	long lines = 0;

	while ((n = getline(&line, &cap, stdin)) != -1)
		lines++;
	if (ferror(stdin))
		perror("getline");
	free(line);
	printf("%ld\n", lines);
	return 0;
}

Two points follow. First, -1 covers both end of file and a read error, so ferror or feof is the only way to tell them apart, and neither case changes who frees line. Second, line and cap must start as a null pointer and zero, or as a buffer obtained from malloc with its real size. A stack array is not acceptable, because getline may pass it to realloc.

The mistake I see most is a free(line) inside an error branch and none on the end-of-file path.

后续讨论 4

顺着彼此的依据读下去
  1. 固定链接

    getline isn't ISO C. It's POSIX, and your snippet only compiles because the default feature macros happen to expose it. Build it with -std=c11 against glibc and the declaration isn't there. An undeclared call hasn't been valid C since C99, and current GCC and Clang make it an error by default, so the loop just doesn't build. #define _POSIX_C_SOURCE 200809L above the first include fixes it. It belongs in the snippet, since people copy the loop and never the build flags.

  2. 固定链接

    One small thing that caught me when handing lines to Lua. Use the return value as the length, never strlen. getline stores whatever bytes the stream had, including a zero byte in the middle, so for the input b\0c\n it returns 4 while strlen(line) says 1. With lua_pushlstring(L, line, n) the script gets all four bytes. With lua_pushstring it quietly gets just "b", which made me laugh once I found it.

  3. 固定链接

    Yes, and the same applies on the C side to any code after the loop that treats line as a string. The returned count is the number of bytes read, including the delimiter when there was one. A last line without a trailing newline comes back without it, so code that strips line[n - 1] unconditionally removes a real character from that line. I will add your input to the test file, together with a final line that has no newline.

同一个问题的其他角度

  1. Assigning realloc back to the only pointer leaks on failure

    *buffer = realloc(*buffer, n) turns up in a lot of growth helpers, and it is wrong in a specific way. If the allocation fails…

    回复 8最近活动 clua
  2. What survives a catalog reload?

    A slice is an address and a length. Neither field keeps the backing allocation alive, and the Zig reference is plain that poi…

    回复 15最近活动 zigswiftrustcppodin
  3. The dangling pointer warning went away, the pointer did not

    Passing a name into a C++ library through a wrapper, and the first version was one line, CString::new(name).unwrap().as_ptr()…

    回复 3最近活动 rustcpp