How store each string of getline() inside a (dynamic) array of strings?

Viewed 118

I'm using the getline() function to get every line of stdin. Every line is a string with different length:

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

int main() {
    char *line = NULL;
    size_t foo = 0;
    ssize_t reader;

    while ((reader = getline(&line, &foo, stdin)) != -1) { // %zu of reader is length of line
        printf("%s", line);
    }

    free(line);
    return 0;
}

In every iteration, line is a string and is containing the current line. How can I take each string-line and store it inside an array? There are several things I have tried but none of them worked or they just lead to memory access failure :(


I hope my question is clear? If it's not, please tell me and I will change it!

2 Answers

Unless you know up front how many lines to expect, then you will have to allocate the array dynamically, eg:

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

int main() {
    char *line = NULL;
    size_t foo = 0;
    ssize_t reader;
    int result = 0;

    int numlines = 0, maxlines = 10;
    char **lines = malloc(sizeof(char*) * maxlines);

    if (!lines) {
        printf("error allocating array\n");
    }
    else {
        while ((reader = getline(&line, &foo, stdin)) != -1) { // %zu of reader is length of line
            printf("%s", line);

            if (numlines == maxlines) {
                maxlines *= 2; // <-- or use whatever threshold makes sense for you
                char **newlines = realloc(lines, sizeof(char*) * maxlines);
                if (!newlines) {
                    printf("error reallocating array\n");
                    result = -1;
                    break;
                }
                lines = newlines;
            }

            lines[numlines] = line;
            ++numlines;

            line = NULL;
            foo = 0;
        }
        free(line); // <-- in case getline() or realloc() failed...

        // use lines up to numlines as needed

        // free lines
        for(int i = 0; i < numlines; ++i) {
            free(lines[i]);
        }
        free(lines);
    }

    return result;
}

You need to create an array of pointers that gets resized when needed:

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

int main()
{
    // start with an array that ends with a NULL pointer
    // (like argv does)
    size_t numLines = 0;
    char **lines = malloc( ( numLines  + 1 ) * sizeof( *lines ) );

    lines[ numLines ] = NULL;

    // break the loop explicitly - easier to handle and much less
    // bug-prone than putting the assignment into a while statement
    for ( ;; )
    {

        // get the next line
        size_t bytes = 0UL;
        char *line = NULL;
        ssize_t result = getline( &line, &bytes, stdin );
        if ( result < 0 )
        {
            break;
        }

        // enlarge the array by one
        numLines++;
        char **tmp = realloc( lines, ( numLines + 1 ) * sizeof( *tmp ) );
        if ( !tmp )
        {
            break;
        }

        lines = tmp;

        // add the new line to the end of the array
        lines[ numLines ] = line;
        lines[ numLines + 1 ] = NULL;
    }

    // use lines - then free them 
    return( 0 );
}

That can be optimized by doing the realloc() calls in chunks, such as every 32 or 64 lines. But given that you're already effectively calling malloc() once per line, that might not help much.

Related