简体   繁体   中英

How to properly allocate true 3D array in C

I am currently struggling to find a proper way to dynamically allocate a true 3D array in C89. The purpose of this is to create a storage for a 3D Cartesian grid. Is it possible to do so without the so-called three-star programming?

The function malloc does not know what kind of an array you are trying to allocate. It just allocates an extent of memory of the size specified by the user.

What you need is to declare correctly the pointer that will point to the allocated memory that you could use an expression like

a[i][j][k]

to access elements of the allocated array where i, j, and k are some indices.

You can allocate a three dimensional array for example the following way

enum { N1 = 2, N2 = 3, N3 = 4 };

int ( *a )[N2][N3] = malloc( sizeof( int[N1][N2][N3] ) );

or

int ( *a )[N2][N3] = malloc( N1 * sizeof( int[N2][N3] ) );

or

int ( *a )[N2][N3] = malloc( N1 * sizeof( *a ) );

or like

int ( *a )[N2][N3] = malloc( N1 * N2 * sizeof( int[N3] ) );

or

int ( *a )[N2][N3] = malloc( N1 * N2 * sizeof( **a ) );

or even like

int ( *a )[N2][N3] = malloc( N1 * N2 * N3 * sizeof( int ) );

or

int ( *a )[N2][N3] = malloc( N1 * N2 * N3 * sizeof( ***a ) );

If your compiler supports variable length arrays then it is not necessary that N1, N2, and N3 would be integer constant expressions.

Here is a demonstrative program.

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

int main(void) 
{
    enum { N1 = 2, N2 = 3, N3 = 4 };
    int ( *a )[N2][N3];
    
    a  = malloc( sizeof( int[N1][N2][N3] ) );
    
    free( a );
    
    a = malloc( N1 * sizeof( int[N2][N3] ) );
    
    free( a );
    
    a = malloc( N1 * sizeof( *a ) );
    
    free( a );
    
    a = malloc( N1 * N2 * sizeof( int[N3] ) );
    
    free( a );
    
    a = malloc( N1 * N2 * sizeof( **a ) );
    
    free( a );
    
    a = malloc( N1 * N2 * N3 * sizeof( int ) );
    
    free( a );
    
    a = malloc( N1 * N2 * N3 * sizeof( ***a ) );
    
    free( a );
    
    return 0;
}

If your compiler does not support variable length arrays and you need to determine sizes of a three-dimensional array at run-time then you can allocate a one-dimensional array and using an index expression simulate a three dimensional array or you will need to allocate arrays of arrays.

For example

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

int main(void) 
{
    enum { N1 = 2, N2 = 3, N3 = 4 };

    int ***a = malloc( N1 * sizeof( int ** ) );
    
    for ( size_t i = 0; i < N1; i++ )
    {
        a[i] = malloc( N2 * sizeof( int * ) );
        for ( size_t j = 0; j < N2; j++ )
        {
            a[i][j] = malloc( N3 * sizeof( int ) );
        }
    }
    
    //...
    
    for ( size_t i = 0; i < N1; i++ )
    {
        for ( size_t j = 0; j < N2; j++ )
        {
            free( a[i][j] );
        }
        free( a[i] );
    }
    
    free( a );
    
    return 0;
}

There are a few options. Some may or may not suit your needs.

Static arrays

If the size of the array is a compile-time constant, you can declare it like this:

int arr[3][4][5];

Variable-length arrays

Same as above, but the array size doesn't need to be a compile-time constant. This only works for local arrays and only in C99, though, so it won't work for you.

int arr[n][m][p];

Or, if you want it dynamically allocated, as suggested by @Eugene Sh:

int (*arr)[m][p] = malloc(sizeof(int) * n * m * p);

Dynamic memory allocation

Using malloc :

int*** arr = malloc(sizeof(int) * n * m * p);

Or, if you don't want triple pointers:

int* arr = malloc(sizeof(int) * n * m * p);

But this array needs to be indexed as arr[i*p + j*m + k] instead of the usual arr[i][j][k] .

By a "true" 3D array I'm assuming you mean that all the elements are in one contiguous block. In that case your best option would be something like this:

/**
 * Allocate an NxRxC array of some arbitrary type T
 */
T (*arr)[R][C] = malloc( sizeof *arr * N );

which gives you

     T (*)[R][C]        T 
     +---+             +---+      
arr: |   | ----------->|   | arr[0][0][0]
     +---+             +---+
                       |   | arr[0][0][1]
                       +---+
                        ...
                       +---+
                       |   | arr[0][0][C-1]
                       +---+
                       |   | arr[0][1][0]
                       +---+
                        ...
                       +---+ 
                       |   | arr[0][R-1][C-1]
                       +---+
                       |   | arr[1][0][0] 
                       +---+
                        ...

You would only use a triple pointer for a piecemeal, "jagged" array allocation (error checking omitted for brevity):

T ***a = malloc( sizeof *a * N )
for ( size_t i = 0; i < N; i++ )
{
  a[i] = malloc( sizeof *a[i] * R );
  for ( size_t j = 0; j < C; j++ )
  {
    a[i][j] = malloc( sizeof *a[i][j] * C );
  }
}

which gives you something like this:

   T ***        T**             T*                  T
   +---+       +---+            +---+              +---+---+---+---+---+
a: |   |------>|   | a[0] ----->|   | a[0][0] ---->|   |   |   |   |   |...
   +---+       +---+            +---+              +---+---+---+---+---+
               |   | a[1] --+   |   | a[0][1] --+  
               +---+        |   +---+           |  +---+---+---+---+---+
                ...         |    ...            +->|   |   |   |   |   |...
                            |                      +---+---+---+---+---+
                            |
                            |   +---+              +---+---+---+---+---+
                            +-->|   | a[1][0] ---->|   |   |   |   |   |...
                                +---+              +---+---+---+---+---+
                                |   | a[1][1] --+
                                +---+           |  +---+---+---+---+---+
                                 ...            +->|   |   |   |   |   |...
                                                   +---+---+---+---+---+

You would use this second method when rows don't have to be the same length, or when you absolutely cannot allocate the whole array in a single contiguous block.

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