Why use char **lineptr
instead of char *lineptr
as a parameter of function getline
?
Imagine the prototype for getline
looked like this:
ssize_t
getline(char *line, size_t n, FILE *stream);
And you called it like this:
char *buffer = NULL;
size_t len = 0;
ssize_t read = getline(buffer, len, stdin);
Before calling getline
, buffer
is null:
+------+
|buffer+-------> NULL
+------+
When getline
is called, line
gets a copy of buffer
because function arguments are passed by value in C. Inside getline
, we no longer have access to buffer
:
+------+
|buffer+-------> NULL
+------+ ^
|
+------+ |
| line +----------+
+------+
getline
allocates some memory with malloc
and points line
to the beginning of the block:
+------+
|buffer+-------> NULL
+------+
+------+ +---+---+---+---+---+
| line +------->+ | | | | |
+------+ +---+---+---+---+---+
After getline
returns, we no longer have access to line
:
+------+
|buffer+-------> NULL
+------+
And we're right back where we started. We can't re-point buffer
to the newly-allocated memory inside getline
because we only have a copy of buffer
.
The prototype for getline
is actually:
ssize_t
getline(char **lineptr, size_t *n, FILE *stream);
And you call it like this:
char *buffer = NULL;
size_t len = 0;
ssize_t read = getline(&buffer, &len, stdin);
&buffer
returns a pointer to buffer
, so we have:
+-------+ +------+
|&buffer+------> +buffer+-------> NULL
+-------+ +---+--+
When getline
is called, lineptr
gets a copy of &buffer
because C is call-by-value. lineptr
points to the same place as &buffer
:
+-------+ +------+
|&buffer+------->+buffer+-------> NULL
+-------+ +---+--+
^
+-------+ |
|lineptr+------------+
+-------+
getline
allocates some memory with malloc
and points the pointee of lineptr
(i.e. the thing lineptr
points to) at the beginning of the block:
+-------+ +------+ +---+---+---+---+---+
|&buffer+------->+buffer+------->+ | | | | |
+-------+ +---+--+ +---+---+---+---+---+
^
+-------+ |
|lineptr+------------+
+-------+
After getline
returns, we no longer have access to lineptr
, but we can still access the newly-allocated memory via buffer
:
+-------+ +------+ +---+---+---+---+---+
|&buffer+------->+buffer+------->+ | | | | |
+-------+ +---+--+ +---+---+---+---+---+