Search code examples
arrayscstructmalloc

How to put an allocated array (sizes known at runtime) in a struct?


To work on 3D arrays with unknown sizes at compile time, I wrote the following program that works :

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

typedef struct {
  int x,y,z;
} dim3D_t;

void print_array(dim3D_t *dim3D, int (*my_array)[dim3D->y][dim3D->z]) {
  for (int i_x=0; i_x<dim3D->x; i_x++)
    for (int i_y=0; i_y<dim3D->y; i_y++)
      for (int i_z=0; i_z<dim3D->z; i_z++)
        printf("%d %d %d : %d\n",i_x,i_y,i_z,my_array[i_x][i_y][i_z]);
}

void alloc_and_init_array(dim3D_t *dim3D, int (**my_array)[dim3D->y][dim3D->z]) {

  *my_array = malloc( sizeof(int) * dim3D->x * dim3D->y * dim3D->z );
  for (int i_x=0; i_x<dim3D->x; i_x++)
    for (int i_y=0; i_y<dim3D->y; i_y++)
      for (int i_z=0; i_z<dim3D->z; i_z++)
        (*my_array)[i_x][i_y][i_z]=100*i_x+10*i_y+i_z;
}

int main() {
  dim3D_t dim3D;
  int (*my_array)[dim3D.y][dim3D.z];
  
  scanf("%d %d %d", &dim3D.x, &dim3D.y, &dim3D.z);  
  alloc_and_init_array(&dim3D, &my_array);
        
  print_array(&dim3D, my_array);
}

I found very convenient to access the array elements with syntax like my_array[][][] and only one memory chunk is allocated.

But, now, I would like to have the array as a member in the structure. How can I do that ?

I would like to avoid 1D array (i.e. to add int *array in the structure member and to access to elements using my_struct.my_array[i_x*dim3D.y*dim3D.z+i_y*din3D.z+i_z]) or to have many memory chunk (arrays of int **, int *).

Edit 1 (answer in fact)

Following the Lundin answer and the good idea of the "cast" trick, I wrote a program that do not use FAM. So, I could have more than one array in my structure. It is very convenient. There is only line to add at the beginning of the functions.

This is the code

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

typedef struct {
  int x,y,z;
  int *array;
} dim3D_t;


void print_array(dim3D_t *dim3D) {

  int (*cast_dim3Darray)[dim3D->y][dim3D->z] = (int(*)[dim3D->y][dim3D->z]) dim3D->array; //cast
  
  for (int i_x=0; i_x<dim3D->x; i_x++)
    for (int i_y=0; i_y<dim3D->y; i_y++)
      for (int i_z=0; i_z<dim3D->z; i_z++)
        printf("%d %d %d : %d\n",i_x,i_y,i_z,cast_dim3Darray[i_x][i_y][i_z]);
}

void alloc_and_init_array(dim3D_t *dim3D) {

  dim3D->array = malloc( sizeof(int) * dim3D->x * dim3D->y * dim3D->z );
  
  int (*cast_dim3Darray)[dim3D->y][dim3D->z] = (int(*)[dim3D->y][dim3D->z]) dim3D->array; //cast
  
  for (int i_x=0; i_x<dim3D->x; i_x++)
    for (int i_y=0; i_y<dim3D->y; i_y++)
      for (int i_z=0; i_z<dim3D->z; i_z++)
        cast_dim3Darray[i_x][i_y][i_z]=100*i_x+10*i_y+i_z;
}

int main() {
  dim3D_t dim3D;
  
  scanf("%d %d %d", &dim3D.x, &dim3D.y, &dim3D.z);  
  alloc_and_init_array(&dim3D); 
        
  print_array(&dim3D);
  
}

Solution

  • Flexible array members only work with one single dimension. However, it is safe to cast to/from them to array pointers since what matters for C's type system is that the variables are accessed through an lvalue of type int. It's a bit cumbersome to work with flexible array members here, since you need to cast. But quite possible.

    Modified program with a bit of explanation in comments:

    #include <stdlib.h>
    #include <stdio.h>
    
    typedef struct {
      size_t x,y,z;
      int array[];
    } dim3D_t;
    
    // the function gets a little bit more readable with a struct:
    void alloc_and_init_array (dim3D_t** dim3D, size_t x, size_t y, size_t z) {
    
      // watch out for people using sizeof *dim3D style here:
      *dim3D = malloc( sizeof(dim3D_t) + sizeof(int[x][y][z]) );
      if(*dim3D==NULL) { /* TODO error handling */ }
    
      (*dim3D)->x = x;
      (*dim3D)->y = y;
      (*dim3D)->z = z;
    
      // cast from the flexible array to an array pointer type with same element type:
      int (*arrptr)[y][z] = (int(*)[y][z]) (*dim3D)->array;
    
      for(size_t i=0; i<x; i++)
        for(size_t j=0; j<y; j++)
          for(size_t k=0; k<z; k++)
            arrptr[i][j][k] = 100*i + 10*j + k;
    }
    
    void print_array (const dim3D_t* dim3D) { // remember const correctness
      // cast here as well:
      int (*arrptr)[dim3D->y][dim3D->z] = (int(*)[dim3D->y][dim3D->z]) dim3D->array;  
    
      // I cooked up a bit more "3D:ish" output:
      for (size_t i_x=0; i_x<dim3D->x; i_x++)
      {
        printf("[ ");
        for (size_t i_y=0; i_y<dim3D->y; i_y++)
        {
          printf("[ ");
          for (size_t i_z=0; i_z<dim3D->z; i_z++)
          {
            printf("%3.1d ", arrptr[i_x][i_y][i_z]);
          }
          printf("] ");
        }
        printf("]\n");
      }
    }
    
    
    int main() {
      size_t x,y,z;
      scanf("%zu %zu %zu", &x, &y, &z);  
      // sanity check x, y, z here....
    
      dim3D_t* dim3D;
      alloc_and_init_array(&dim3D, x, y, z);
      print_array(dim3D);
    
      free(dim3D); // remember to clean up
    }
    

    Input:

    2 2 3
    

    Output:

    [ [   0   1   2 ] [  10  11  12 ] ]
    [ [ 100 101 102 ] [ 110 111 112 ] ]