我有一个变量N
。 我需要一个6xNxN
的数组。
类似这样:
int arr[6][N][N];
但是,显然这样行不通。
我不确定如何进行分配,以便在N
为5时可以访问例如arr[5][4][4]
,如果N
为24,则访问arr[5][23][23]
。
请注意,N
永远不会改变,因此我永远不需要重新分配arr
。
我该怎么办?int ***arr = malloc(6 * N * N * sizeof(int));
能用吗?
我有一个变量N
。 我需要一个6xNxN
的数组。
类似这样:
int arr[6][N][N];
但是,显然这样行不通。
我不确定如何进行分配,以便在N
为5时可以访问例如arr[5][4][4]
,如果N
为24,则访问arr[5][23][23]
。
请注意,N
永远不会改变,因此我永远不需要重新分配arr
。
我该怎么办?int ***arr = malloc(6 * N * N * sizeof(int));
能用吗?
您可以将三维数组分配到堆上,如下所示:
int (*arr)[N][N] = malloc(sizeof(int[6][N][N]));
使用完毕后,您可以自由地进行操作。
free(arr);
另一种与@StoryTeller建议相同的写法是 -
int (*arr)[N][N] = malloc(6u * sizeof(*arr));
但是在这里,您需要注意在6
后面加上u
以防止有符号算术溢出。
此外,在像@chqrlie建议的那样size_t
比int
宽度小的平台上仍可能存在问题,但在“大多数”常用平台上不会出现这种情况,因此您可以放心使用它。
malloc(6u * sizeof *arr);
会更符合惯用语。尽管这种方法展示了“数组类型”的好处。 - StoryTeller - Unslander Monicamalloc(6 * N * N * sizeof(int))
的真正问题在于在转换为 size_t
之前,6 * N * N
可能会导致溢出。更通用和更安全的替代方法是:int (*arr)[N][N] = malloc(sizeof(*arr) * 6);
。 - chqrlie6 * sizeof(*arr)
中,唯一可能发生有符号溢出的情况是当size_t
比int
小不超过3位时。我不确定这是否符合标准。 - chqrlieint arr[6][N][N];
可以正常工作。你只需要将编译器和C语言知识更新到1999年或之后,因为变长数组(VLA)在该语言中被引入。
(如果你使用的是GCC 5.0以下版本,你必须明确告诉它不要使用古老的C标准,通过传递-std=c99
或-std=c11
参数来实现。)
另外,如果你需要堆分配,可以这样做:
int (*arrptr)[Y][Z] = malloc( sizeof(int[X][Y][Z]) );
由于 int***
不能指向 3D 数组,因此您无法执行 int ***arr = malloc(6 * N * N * sizeof(int));
。一般来说,超过两个间接级别是您的程序设计完全有缺陷的明确标志。
有关详细信息,请参见:正确分配多维数组。
size_t
参数。VLA 受到了很多批评,例如因为你无法从 OOM 条件中恢复。这篇 Usenet 帖子(当然不是权威的)有一个很好的列表。 - user2371524void func (size_t n, int array[n])
,而不是 void func(int* array, size_t n)
,后者是老式样式。现代化的惯用 C 语言是 int (*arr)[y] = malloc( sizeof(int[x][y] );
,而不是 int* arr = malloc( x*y*sizeof(int) );
,后者是 C90 样式的混乱数组。等等。现代版本要求可以使用指向 VLA 类型的指针。不使用它们就意味着回到了上世纪 90 年代的 C 编程风格。 - Lundinsizeof
、_Alignof
和&
一起使用),因此在数组上的索引按照您的期望工作。int a[42];
a
评估为类型为int *
的指针,索引的工作方式如下:a [18] => *(a + 18)
。"int a[16][42];
a
的 元素 类型为 int ()[42]
(42 个 int 元素的数组)。根据上述规则,在大多数情况下,计算此类型的表达式会再次生成一个 int *
指针。但是对于 a
本身呢?它是一个 int ()[42]
数组,因此 a
将评估为指向 42 个 int 元素的数组的指针:int (*)[42]
。然后让我们看一下索引运算符的作用:a[3][18] => *(*(a + 3) + 18)
假设a
是类型为int (*)[42]
的地址,这个内部加法可以正确地加上42 * sizeof(int)
。如果该类型中的第二个维度未知,则这将是不可能的。
我猜对于n维情况,也很容易推导出类似的例子。
Use a dynamically allocated flat array with size 6*N*N
. You can calculate the indices yourself if you save N
somewhere.
Somewhat less efficient, but yielding better readable code, you could use an array of pointers to arrays of pointers to int (multiple indirection). You could e.g. do
int ***a = malloc(6 * sizeof *int);
for (size_t i = 0; i < 6; ++i)
{
a[i] = malloc(N * sizeof *(a[i]));
for (size_t j = 0; j < N ++j)
{
a[i][j] = malloc(N* sizeof *(a[i][j]));
}
}
// add error checking to malloc calls!
Then your accesses will look just like those to a normal 3d array, but it's stored internally as many arrays with pointers to the other arrays instead of in a big contiguous block.
I don't think it's worth using this many indirections, just to avoid writing e.g. a[2*N*N+5*N+4]
to access the element at 2,5,4
, so my recommendation would be the first method.
malloc
,可以轻松解决你的问题。int ***arr = malloc(6 * N * N * sizeof(int));
然而,int ***
是不必要的(也是错误的)。使用一个平坦的数组,这很容易分配:
int *flatarr = malloc(6 * N * N * sizeof(int));
flatarr[(X*N*N) + (Y*N) + Z]
来代替像问题中那样访问arr[X][Y][Z]
。事实上,你甚至可以编写一个方便的宏:#define arr(X,Y,Z) flatarr[((X)*N*N) + ((Y)*N) + (Z)]
这基本上是我在我的语言Cubically中所做的,以允许多种尺寸的立方体。感谢Programming Puzzles & Code Golf用户Dennis给了我这个想法。
int arr [6] [N] [N];
实际上是有效的,但它也使用了大量堆栈空间,并且存在堆栈溢出的风险,因此最好使用malloc分配数组。(int ***arr = ...
是错误的。) - user2357112int ***arr = malloc(6 * N * N * sizeof(int));
这样是不行的。 - Ajay Brahmakshatriyaint ***arr = ...
是错误的,那正确的是什么? - No oneint
比size_t
小,那么6 * N * N * sizeof(int)
可能会出现溢出的问题,在当前的64位体系结构上这种情况非常普遍。我曾经由于这种类型的问题而受挫。例如:int N = 30000;
应该分配大约21亿字节的空间,这可能适合内存,但是6 * N * N
超过了32位的INT_MAX
,在转换为size_t
之前最多只能包裹,并且malloc
的值比预期的要小得多。为避免此类问题,请始终将sizeof()
运算符放在第一位。 - chqrlie