Having Fun with the getline() Function

In last week’s post, I updated information I had originally written about the useful getline() function. The topic is worthy of further exploration, specifically with the mechanics behind the function’s internal allocation — and reallocation — of memory.

The getline() function allocates memory when passed a char pointer initialized to NULL. If more storage is required, the function automatically re-allocates the pointer. To demonstrate how this internal operation behaves, I concocted the following code.

2022_07_16-Lesson-a.c

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

int main()
{
    char *buffer = NULL;
    size_t size;
    int r;

    /* repeat until a blank line is input */
    do
    {
        /* prompt and gather input */
        printf("Type: ");
        r = getline( &buffer, &size, stdin );

        /* show input */
        printf("You typed (%d): %s",r,buffer);

    } while( r>1 );

    return(0);
}

The above code doesn’t even bother to initialize variable size; it doesn’t matter! The getline() function allocates as much storage as needed.

At Line 15, the function returns the number characters input, including the newline. The value -1 is returned upon error and zero for an empty string (which can’t be input directly from the keyboard). The do-while loop repeats until a blank line is input (r is one or less). Here’s a sample run:

Type: This is some text
You typed (18): This is some text
Type: Even more text
You typed (15): Even more text
Type:
You typed (1):

This code demonstrates that the function can be called again and again, each time processing the text input regardless of the extant values of buffer and size. I wondered whether the buffer is allocated afresh for every call, in which case it could be saved. This way getline() can process several lines of text while reusing the same variables. So I wrote this code:

2022_07_16-Lesson-b.c

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

int main()
{
    const int range = 5;
    char *buffer = NULL;
    char *text[range];
    size_t size;
    int x;

    /* gather `range` lines of input */
    for( x=0; x<range; x++ )
    {
        printf("Line #%d/%d: ",x+1,range);        /* add one for humans */
        getline( &buffer, &size, stdin );
        text[x] = buffer;
    }

    /* output the text */
    for( x=0; x<range; x++ )
    {
        printf("%s",text[x]);
    }

    return(0);
}

In this update to the code, the first for loop uses getline() to fetch five lines of text. The buffer for each allocated line is saved in the text[] char pointer array. The second loop outputs each of the stored lines. But it doesn’t really work:

Line #1/5: Line one
Line #2/5: This is line two
Line #3/5: Line three
Line #4/5: Almost done
Line #5/5: Last line!
Line one
Last line!
Last line!
Last line!
Last line!

Because the second line is longer than the first, buffer is reallocated by getline(). As the remaining lines are shorter, buffer isn’t reallocated. Even then, copying the address of buffer into text[x] (at Line 17) is a poor way to retain the text input. It relies upon the assumption that each call to getline() allocates another buffer, which demonstrably isn’t the case.

The moral of the story is that you can use getline() to capture a single line of text, use the text in some manner, then use getline() again with the same arguments. Never assume that a new buffer is created for each call. Further, to save the text you must allocate a new buffer and copy the text into it.

Oh, and you can free the buffer after you’ve used it. I don’t in my examples because getline() appears only in the main() function, where memory is freed when the program terminates.

Leave a Reply