如何在 C 中正确分配真正的 3D 数组

How to properly allocate true 3D array in C

我目前正在努力寻找一种在 C89 中动态分配真实 3D 数组的正确方法。这样做的目的是为 3D 笛卡尔网格创建存储。没有所谓的三星级节目能做到吗?

有几个选项。有些可能适合也可能不适合您的需要。

静态数组

如果数组的大小是编译时常量,可以这样声明:

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

变长数组

同上,但数组大小不需要是编译时常量。不过,这仅适用于本地数组,并且仅适用于 C99,因此对您不起作用。

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

或者,如果您希望按照@Eugene Sh 的建议动态分配它:

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

动态内存分配

使用malloc

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

或者,如果您不想要三重指针:

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

但是这个数组需要索引为 arr[i*p + j*m + k] 而不是通常的 arr[i][j][k].

函数 malloc 不知道您要分配的数组类型。它只是分配用户指定大小的内存范围。

你需要的是正确声明指向分配内存的指针,你可以使用像

这样的表达式
a[i][j][k]

访问分配数组的元素,其中 i、j 和 k 是一些索引。

你可以分配一个三维数组,例如下面的方式

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

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

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

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

或喜欢

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

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

甚至喜欢

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

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

如果您的编译器支持可变长度数组,则 N1、N2 和 N3 不必是整型常量表达式。

这是一个演示程序。

#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;
}

如果您的编译器不支持变长数组,而您需要在 运行 时确定三维数组的大小,那么您可以分配一个一维数组并使用索引表达式模拟一个三维数组,否则您将需要分配数组的数组。

例如

#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;
}

对于“真正的”3D 数组,我假设您的意思是所有元素都在一个连续的块中。在那种情况下,您最好的选择是这样的:

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

这给了你

     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] 
                       +---+
                        ...

您只能将三重指针用于零碎的、“参差不齐”的数组分配(为简洁起见省略了错误检查):

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 );
  }
}

这给你这样的东西:

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

当行不必具有相同的长度时,或者当您绝对不能在单个连续块中分配整个数组时,您可以使用第二种方法。