简体   繁体   中英

using malloc for multidimensional array of struct

This is probably a basic question but I want to allocate the memory for 3 dimensional array of a struct. I'm trying to read doubles from a file and want to store in struct. The first line is block number (not relevant here as it'll be 1 always), second line denotes the number of grid points in X, Y and Z coordinate respectively. In this case 10 points in X, 5 in Y and 1 in Z direction. And from third line, are the X,Y,Z coordinates of each points which are the doubles I would like to read. First there are all X components (ie 10*5*1 x coordinates, then similarly Y and Z). The file format is like this:

      1
      10        5        1
  0.000000e+00   1.111111e+00   2.222222e+00   3.333333e+00 
  4.444445e+00   5.555555e+00   6.666667e+00   7.777778e+00 
  8.888889e+00   1.000000e+01   0.000000e+00   1.111111e+00 
  2.222222e+00   3.333333e+00   4.444445e+00   5.555555e+00 
  6.666667e+00   7.777778e+00   8.888889e+00   1.000000e+01 
  0.000000e+00   1.111111e+00   2.222222e+00   3.333333e+00 
  4.444445e+00   5.555555e+00   6.666667e+00   7.777778e+00 
  8.888889e+00   1.000000e+01   0.000000e+00   1.111111e+00 
  2.222222e+00   3.333333e+00   4.444445e+00   5.555555e+00 
  6.666667e+00   7.777778e+00   8.888889e+00   1.000000e+01 
  0.000000e+00   1.111111e+00   2.222222e+00   3.333333e+00 
  4.444445e+00   5.555555e+00   6.666667e+00   7.777778e+00 
  8.888889e+00   1.000000e+01...and so on...

I can read the first 4 integers and hence I know the number of points I wish to store data for. Then I'm using malloc function to allocate the memory and store the data in the variables. When I execute the program, it reads the integers but fails to read the doubles. What is the mistake I'm making?

Here's my code:

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

typedef struct{
    double x,y,z;
}Mesh;

int main(void)
{
    int nblocks, IMAX, JMAX, KMAX;

    Mesh ***grid;

    FILE *mesh = fopen("test.x","r");

    fscanf(mesh,"%i %i %i %i",&nblocks,&IMAX,&JMAX,&KMAX);
    printf("%i %i %i %i\n",nblocks,IMAX,JMAX,KMAX);

    grid = malloc(sizeof(Mesh)*nblocks*IMAX*JMAX*KMAX);

    fscanf(mesh,"%lf",&grid[0][0][0].x);
    printf("%lf\n",grid[0][0][0].x);

    fclose(mesh);

    return 0;
}

The program doesn't give any error while compiling but it does't read/write the variable I stored in the x variable of the struct. (If this works, I can put it in loop for reading all values which I've not done here.)

If I define Mesh grid[IMAX][JMAX][KMAX] after I read in IMAX,JMAX,KMAX , I get correct output. But wanted to know how pointer way of doing works.

Thank you, Pranav

The problem is that you define grid as a pointer to a pointer to a pointer to struct, but you think that you have a three dimensional array of contiguous elements.

Explanation of the error

The following array:

Mesh array[10][5][1];   // what you would like to manage dynamically

would be stored in memory like this:

+----+----+----+----+----+----+----+----+----+----+----+----+-----
|A000|A010|A020|A020|A030|A040|A100|A110|A120|A120|1030|A140|....
+----+----+----+----+----+----+----+----+----+----+----+----+-----

But the following pointer to pointer to pointer

Mesh ***grid;           // How you decladed it 

is managed as if it would be like this:

grid-->  +--+--+--+--+--+--+--+
         | 0| 1| 2|........| 9|   some pointers to "pointers to struct"
         +--+--+--+--+--+--+--+
   grid[0] |
           +--->  +--+--+------+
                  | 1| 2| .... |   some pointers to struct 
                  +--+--+------+
         grid[0][0] |
                    +--->  +--+--+------+
                           | 1| 2| .... |   some struct 
                           +--+--+------+
               grid[0][0][0] |
                             +--->  +----+----+----+----
                                    |A000|A010|A020|... some struct  
                                    +----+----+----+----

Your malloc() allocates an array of contiguous IMAX*JMAX*KMAX struct elements. In reality, it's like a one dimensional array, because malloc() and your compiler do not know anything about the dimensions and their respecive size.

But when you write grid[0], your code looks at the adress pointed to by grid and expects to find a pointer there (but it's yet only uninitialised struct) and so on. So you might write at a random place in memory and get a segmentation fault.

Solution

You must manage grid as a one dimentional array of struct, and organise the indexing explicitely in your code.

So declare:

Mesh *grid; 

And whenever you think of element [i][j][k] of your mesh, write:

grid[(i*JMAX+j)*KMAX+k]  

Miscellaneous remarks:

You could use calloc(IMAX*JMAX*KMAX, sizeof(Mesh)) because it makes clear that it's an array, and the memory bloc is set to 0.

By the way, (may be you have it already in your real code) as a reflex always check if the allocation succeeds, and foresee a free() when you no longer need it.

You have a four dimensional array, not three. The dimensions are:

  1. nblocks
  2. IMAX
  3. JMAX
  4. KMAX

Hence, the type for grid has to be Mesh**** , not Mesh*** .

Mesh ****grid;

Your code to allocate memory for grid has to be:

grid = malloc(nblocks * (sizeof *grid));
for ( block = 0; block < nblocks; ++block )
{
   grid[block] = malloc(IMAX * (sizeof *grid[0]));
   for ( i = 0; i < IMAX ; ++i )
   {
      grid[block][i] = malloc(JMAX * (sizeof *grid[0][0]));
      for ( j = 0; j < JMAX ; ++j )
      {
         grid[block][i][j] = malloc(KMAX * (sizeof *grid[0][0][0]));
      }
   }
}

Now, you can access the grid data using:

grid[block][index][jindex][kindex]

These are valid usages:

fscanf(mesh,"%lf",&grid[0][0][0][0].x);
printf("%lf\n",grid[0][0][0][0].x);

A simple option is to write:

Mesh (*grid)[IMAX][JMAX][KMAX] = malloc( nblocks * sizeof *grid );

Then to access the items:

grid[block_num][i][j][k] = 5;

You indicated in a comment that you don't really need the nblocks since it is always 1 , in that case you could go:

Mesh (*grid)[JMAX][KMAX] = malloc( IMAX * sizeof *grid );
// ...
grid[i][j][k] = 5;

Since C99 it is permitted for array dimensions to not be compile-time constants. 1

Note the use of the idiom ptr = malloc(N * sizeof *ptr); , this guarantees that we allocate N of whatever type ptr points to, so we can be sure that we allocated the right number of bytes even though the type of ptr is complicated.


1 This feature was required in C99 but changed to optional in C11. If you are on a compiler that is C11 compliant but does not have VLA (I don't know of any) you would have to go for one of the other solutions.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM