HGitere
HGitere

Reputation: 37

Printing an array of pointers to strings using C leaves out the last string

I am looking to create an array of pointers to strings read from a file in C. However when I try to print out the strings copied to stdout, the last line of the file is always left out. The program also sometimes experiences a segmentation fault which I haven't been able to completely eliminated. It happens about 2 out of 5 times.

Here is my input.c code:

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "input.h"

#define MAXLINES 5000


void writelines(char *arr[], int l);

char *read_lines[MAXLINES];

void get_input(const char *fp) {
    FILE *contents;
    char *line;
    char *temp;
    size_t len;
    ssize_t read;
    int i;
    i = 0;
    contents = fopen(fp, "r");
    if (contents == NULL)
        exit(EXIT_FAILURE);
    while ((read = getline(&line, &len, contents)) != -1) {
        if ((temp = (char *) malloc(strlen(line) + 1)) == NULL) {
            printf("Could not allocate required memory.");
            exit(EXIT_FAILURE);
        }
        else {
            line[strlen(line) - 1] = '\0';
            strcpy(temp, line);
            read_lines[i++] = temp;
        }
    }
    fclose(contents);
    free(line);
    free(temp);
    writelines(read_lines, i);
    exit(EXIT_SUCCESS);
}

void writelines(char *arr[], int l) {
    int i;

    for (i = 0; i < l; i++) {
        printf("%s\n", arr[i]);
    }
}

My main.c file is:

#include <stdio.h>
#include "input.h"

int main(int argc, char *argv[]) {
    if (argc == 1)
        printf("Please provide a valid source code file.\n");
    else
        get_input(*(++argv));

    return 0;
}

I compile using gcc main.c input.c -Wall with no warnings or errors.

Using gdb I can confirm that the process runs normally.
When it experiences a segmentation fault, the back trace shows a call to strlen that apparently fails.

Upvotes: 1

Views: 49

Answers (1)

Jean-Fran&#231;ois Fabre
Jean-Fran&#231;ois Fabre

Reputation: 140168

from the documentation:

If *lineptr is NULL, then getline() will allocate a buffer for storing the line, which should be freed by the user program. (In this case, the value in *n is ignored.)

but in your case you're passing an uninitialized value to getline the first time, so getline thinks it can write to that illegal location and this is undefined behaviour (which explains the "It happens about 2 out of 5 times" thing)

The first fix should be to initialize line:

char *line = NULL;

then, why are you creating a copy of line, and you're not freeing line (memory leak) and you're not resetting it to NULL. So next time getline reuses the previous buffer, which may not be long enough to hold the next line.

The fix is just to store the line:

read_lines[i++] = line;

then set line = NULL so getline allocates the proper len for next line. And drop the malloc code, it's useless.

fixed part (you don't need to pass pointer on len it is ignored):

line = NULL;
while ((read = getline(&line, NULL, contents)) != -1) {
         read_lines[i++] = line;
         line[strcspn(line, "\n")] = 0;  // strip off linefeed if there's one
         line = NULL;
 }

(linefeed strip adapted from Removing trailing newline character from fgets() input)

Upvotes: 1

Related Questions