Aditya
Aditya

Reputation: 65

How to return 2d char array (char double pointer) in C?

I am reading a file that contains several lines of strings(max length 50 characters). To store those strings I created a char double-pointer using calloc. The way my code works is as it finds a line in the file it adds one new row (char *) and 50 columns (char) and then stores the value.

My understanding is that I can call this method and get this pointer with values in return. However, I was not getting the values so I check where I am losing it and I found that the memory is not persisting after while loop. I am able to print strings using print 1 statement but print 2 gives me null.

Please let me know what I am doing wrong here.

char **read_file(char *file)
{
    FILE *fp = fopen(file, "r");
    char line[50] = {0};
    char **values = NULL;
    int index = 0;
    if (fp == NULL)
    {
        perror("Unable to open file!");
        exit(1);
    }

    // read both sequence
    while (fgets(line, 50, fp))
    {
        values = (char **)calloc(index + 1, sizeof(char *));
        values[index] = (char *)calloc(50, sizeof(char));
        values[index] = line;
        printf("%s",values[index]); // print 1
        index++;
    }
    fclose(fp);
    printf("%s", values[0]); // print 2
    return values;
}

Upvotes: 0

Views: 211

Answers (1)

user9706
user9706

Reputation:

  1. line content is overwritten on each loop iteration (by fgets()).
  2. values is overwritten (data loss) and leaks memory on each iteration index > 1.
  3. value[index] is allocated memory on each iteration which leaks as you overwrite it with the address of line on the following line.
  4. line is a local variable so you cannot return it to caller where it will be out of scope.
  5. caller has no way to tell how many entries values contain.

Here is a working implementation with a few changes. On error it closes the file and frees up memory allocated and return NULL instead of exiting. Moved printf() to caller:

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

#define BUF_LEN 50

char **read_file(char *file) {
    FILE *fp = fopen(file, "r");
    if(!fp) {
        perror("Unable to open file!");
        return NULL;
    }
    char **values = NULL;
    char line[BUF_LEN];
    unsigned index;
    for(index = 0;; index++) {
        char **values2 = realloc(values, (index + 1) * sizeof(char *));
        if(!values2) {
            perror("realloc failed");
            goto err;
        }
        values = values2;
        if(!fgets(line, BUF_LEN, fp)) break;
        values[index] = strdup(line);
    }
    fclose(fp);
    values[index] = NULL;
    return values;
err:
    fclose(fp);
    for(unsigned i = 0; i < index; i++) {
        free(values[i]);
    }
    free(values);
    return NULL;
}

int main() {
    char **values = read_file("test.txt");
    for(unsigned i = 0; values[i]; i++) {
        printf("%s", values[i]);
        free(values[i]);
    }
    free(values);
    return 0;
}

fgets() returns line ending in '\n' or at most BUF_LEN - 1 of data. This means a given value[i] may or may not be ending with a \n. You may want this behavior, or you want value[i] to be consistent and not contain any trailing \n irregardless of the input.

strdup() is _POSIX_C_SOURCE >= 200809L and not standard c, so if you build with --std=c11 the symbol would not be defined.

Upvotes: 1

Related Questions