如何在 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] --+
+---+ | +---+---+---+---+---+
... +->| | | | | |...
+---+---+---+---+---+
当行不必具有相同的长度时,或者当您绝对不能在单个连续块中分配整个数组时,您可以使用第二种方法。
我目前正在努力寻找一种在 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] --+
+---+ | +---+---+---+---+---+
... +->| | | | | |...
+---+---+---+---+---+
当行不必具有相同的长度时,或者当您绝对不能在单个连续块中分配整个数组时,您可以使用第二种方法。