C: Using a 2D array created in a function during main

Viewed 207

I have looked through similarly worded questions and had no luck with my problem. I am aiming to read in a matrix from a text file in C, and then perform operations on it. Currently, I am writing a function to read in the matrix. The text files always have the format

# Comment
# Comment
matrix NROWS NCOLS
DATA DATA DATA
DATA DATA DATA etc
end

so for a 3x4 matrix:

# Comment
# Comment
matrix 3 4
7.16669652013   8.21223145665   8.78374366033   5.87828106521   
7.32124040244   4.30552353817   1.67905652974   3.91825198378   
7.28717047595   3.83999063812   5.53693148121   6.03363152874
end

My function currently is:

void read_matrix(char *filename, int rows, int cols) {
    char *status;
    char line[MAX_FILE_LINE_SIZE];
    int line_no = 1;
    char *data_line;
    double matrix[rows][cols];

    printf("Reading Matrix...\n");
    FILE *inf = fopen(filename, "r");
    if (!inf) {
        fprintf(stderr, "Error, could not open file '%s'\n", filename);
        exit(1);
    }
    
    status = fgets(line, sizeof(line), inf);
    while (status) {
        status = fgets(line, sizeof(line), inf);
        ///printf("%i: %s", line_no, line);
        if (line_no >= 3 && line_no < 3+rows) {
            int i=0;
            data_line = strtok(line, " \t");
            while (data_line != NULL) {
                matrix[line_no-3][i] = atof(data_line);
                data_line = strtok(NULL, " \t");
                i++;
            }
        }
        line_no ++;
    }
    printf("PRINT MATRIX\n");
    for (int z=0; z<rows; z++) {
        for (int j=0; j<cols; j++) {
            printf("matrix[%i][%i] = %f\n",z, j, matrix[z][j]);
        }
    }
    fclose(inf);
}

which works great, and so at the end of the function I have all the data saved into a 2D array matrix[rows][cols]. However I am completely stumped on how to then use this function in main, as outside of the function the variable is no longer in scope. Any help would be very appreciated.

3 Answers

VLA (variable length arrays) are not advised in C. Add an "allocate_matrix" function, which would return a double ** matrix allocated with malloc, and return it to main ! Dont forget to free everything after use ;)

"However I am completely stumped on how to then use this function in main, as outside of the function the variable is no longer in scope"

You have correctly identified the problem as scope. It can be addressed in several ways. I like the following:

    double ** read_matrix(char *filename, int *rows, int *cols);//requires memory allocation

In this, the function will return array, and filename is passed as a parameter, and because you do not know the size parameters until the file is read, pass pointers to rows and cols. It would be called as:

int main(void)
{
     const char filename[] = {"c:\\somefile.txt"};
     double **array = NULL;
     int rows, cols;
     ... other variables
     array = read_matrix(filename, &rows, &cols);
     if(array)
     {
          // use array
          ...
          free(array);
     }

The internals of read_matrix() include:

  • reading and parsing the file to get values for rows, cols and data for array.
  • using rows, cols, allocate memory to double **array. (malloc() used)
  • populate new dynamically allocated array with data values.
  • set *cols == to internally determined value for columns.
  • set *rows == to internally determined value for rows.
  • return array.

And, back in main(),

  • use the array object.
  • free(array);

Inside the read_matrix() function, the memory allocation that is called could (and probably should) be implemented as another function, such as:

double ** Create2D(int c, int r)
{   
    double **arr;
    int    y;
    
    arr   = calloc(c, sizeof(double *));
    for(y=0;y<c;y++)
    {
        arr[y] = calloc(r, sizeof(double)); 
    }
    return arr;
}

Called as:

double ** read_matrix(char *filename, int *rows, int *cols)
{
     int cols=0, rows=0, i, j;
     
     //variables, initializations
     //read and parse files
     //convert values read from file from text into `cols` and `rows`
     //allocate memory for double **array
     //place data values from file into array
     double **array = Create2D(rows, cols
     if(array)
     {
         for(i=0;i<rows;i++)
         {
             for(j=0;j<cols;j++)
             {
                  array[i][j] = data[i][j];
             }
         }
     }
     return array;  //must test that array is not null in caller
 }

Where could Create2D() be implemented as:

double ** Create2D(int r, int c)
{   
    double **arr;
    int    y;
    
    arr   = calloc(c, sizeof(double *));
    for(y=0;y<c;y++)
    {
        arr[y] = calloc(r, sizeof(double)); 
    }
    return arr;
}                 

you will need to dynamically allocate your matrix with malloc and return it from your function, that way you will be able return it from the function and access the information you saved outside of the scope, there is no way to do this in C without dynamically allocating it. here's an answer from another user to show how to dynamically allocate a matrix: https://stackoverflow.com/a/2128754/6365408 the intuitive way is to allocate a pointer pointer to a double the size of columns, and in each index allocate a row in a for loop:

int **mat = (int **)malloc(rows * sizeof(int*));
for(int i = 0; i < rows; i++) mat[i] = (int *)malloc(cols * sizeof(int));

but you lose on locality of reference which will affect performance. the 2nd option is:

int *mat = (int *)malloc(rows * cols * sizeof(int));

Then, you simulate the matrix using

int offset = i * cols + j;
// now mat[offset] corresponds to m(i, j)

for row-major ordering and

int offset = i + rows * j;
// not mat[offset] corresponds to m(i, j)

in the end you will have to free all the allocations you've made with malloc.

Related