Ankit Kulshrestha Ankit Kulshrestha - 1 month ago 4
C Question

How to use a 2D array as an output parameter in a function in C

I'm writing a program to compute the shortest path between two points on a graph using Floyd's algorithm. I'm required to write to a binary file and then read from it and print the graph. It is here that my problem lies, since my function signature has to be of the form:

void read_graph(char *filename, int *n, int ***A)
.

I've written the following:

void read_graph(char *filename, int *n, int ***A)
{
FILE *fin;
size_t i;
int x;
int **arr;

fin = fopen(filename, "rb");
if (fin == 0) {
printf("Error opening file\n");
exit(1);
} else {
fread(&x, sizeof(int), 1, fin);
printf("Read size of matrix %d\n",x);
*n = x;
fseek(fin, sizeof(int)+1, SEEK_SET);
arr = malloc(x*sizeof(int *));
if (arr == NULL) {
printf("Not enough space.\n");
}
for (i = 0; i < x; ++i) {
arr = malloc(x*sizeof(int));
}
for (i = 0; i < x; ++i) {
fread(arr,sizeof(int), x, fin);
}

// ***A = **arr;
}
fclose(fin);
free2darray(*A, x);
}


The
free2darray
is a utility function to free the memory. My problem is that this function works if I allocate a 2d array before calling this. However, it is required that this function returns a 2d array to
***A
and not the other way round. If you could tell me what I'm doing wrong, it would be super helpful. Also criticism on the code style are welcome.

EDIT: Here's how I'm calling the function currently:

int **arr;
int x;
size_t i;
arr = malloc(5*sizeof(int*));
if (arr == NULL) {
printf("Cannot allocated memory for array\n");
exit(1);
}
for (i = 0; i < 5; i++) {
arr[i] = malloc(5*sizeof(int));
}
read_graph("test.dat", &x, &arr);


5 is just a test size. The
test.dat
was previously written by populating a 5*5 array with random values.

Thanks

Answer

You were close. If you read my answer a few minutes ago, you might have noticed that my solution returned a pointer to the dynamic array. Then I saw in the comments to your question that the function prototype was a given, so I have revised my answer.

All you need to do in main() is declare a pointer to int to store the size of the matrix, and a pointer to a pointer to int, which will be passed to the function read_graph(). I included a few lines here to display the contents of the new array.

You need to free the allocated memory before exiting. This was one of the main problems that you had in the original code: you were freeing the memory at the end of the call to read_graph(). You need to wait until you don't need the array contents any more before you free these allocations.

The other problem that I found was that when you allocated memory for the individual rows of the dynamic array, you forgot to index arr, so you malloced memory and assigned the address to the same pointer x times. This is your loop that repeatedly reassigns memory to arr (and is a memory leak because the reference to the previous allocations is lost):

for (i = 0; i < x; ++i) {
    arr = malloc(x*sizeof(int));
}

The pointer A that is passed into the function is used to receive the results of calls to malloc(), so that the allocated memory is visible from the calling function. This changes some of your assignments a bit, since you had declared a pointer in your original code for this purpose. I changed the loop that reads values into the array so that it reads in one value at a time... it looks like you were trying to read in x at a time.

You don't really need the fseek() since the file pointer is already in position, so I removed that line. I also added a check to make sure that the data file closes properly. I made up a test data file, and everything seems to work. I also changed your size_t variables to ints. I did this because there were comparisons of int values with size_t values in the loops that were generating compiler warnings. The code still works fine, but no sense ignoring warnings. I would have changed everything to size_t, but you read the size of the matrix from your file, and that is an int in your code. If you can change your file format a bit, the size of the matrix could be stored as a size_t value. Here is the revised code:

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

void read_graph(char *filename, int *n, int ***A);

int main(void)
{
   int x;
   int **arr;

   read_graph("test.dat", &x, &arr); 

   for (int i = 0; i < x; i++) {
       for (int j = 0; j < x; j++)
           printf("%d ", arr[i][j]);
       putchar('\n');
   }

   free2darray(*A, x);

   return 0;
}

void read_graph(char *filename, int *n, int ***A)
{
    FILE *fin;
    int i;
    int x;

    fin  = fopen(filename, "rb");
    if (fin == 0) {
        printf("Error opening file\n");
        exit(1);
    } else {
        fread(&x, sizeof(int), 1, fin);
        printf("Read size of matrix %d\n",x);
        *n = x;

        *A = malloc(x*sizeof(int *));
        if (*A == NULL) {
            printf("Not enough space.\n");
        }
        for (i = 0; i < x; ++i) {
            (*A)[i] = malloc(x*sizeof(int));
        }
        for (i = 0; i < x; ++i)
            for (int j = 0; j < x; ++j)
                fread(&(*A)[i][j], sizeof(int), 1, fin);

    }

    if (fclose(fin) != 0)
        fprintf(stderr, "Unable to close file\n");

}

Edit

I had to create a test file, so I added some code for this purpose that is not shown above. It reads a text file and creates a binary file that the program can use. The resulting binary file is just a sequence of ints. I just put the code at the beginning of main(), and created a file called "testdat.txt" in the same directory as "a.out". My text file contained a 5 on the first line, and then 25 integers, one on every line:

5
1
2
3
4
5
....

And here is the code:

/* Code to create a datafile from a text file */

FILE *fpin, *fpout;
char nstr[100];
int num_out;

fpin = fopen("testdat.txt", "r");
fpout = fopen("test.dat", "wb");
while (fgets(nstr, 100, fpin) != NULL) {
    num_out = atoi(nstr);
    fwrite(&num_out, sizeof(int), 1, fpout);
}
fclose(fpin);
fclose(fpout);