我在用C编写的俄罗斯方块程序中遇到了一个大问题。我正在尝试使用4D多维阵列,例如
uint8_t shape[7][4][4][4]
但当我尝试这种方法时,我一直会遇到seg错误,我四处阅读,似乎我用光了这种数组的所有堆栈内存(我所做的只是用0和1填充数组来描绘一个形状,这样我就不会输入高得离谱的数字或其他什么)。
这是它的一个版本(在pastebin上,因为你可以想象它非常丑陋和长)。如果我把数组做得更小,它似乎可以工作,但我试图避免绕过它,因为理论上每个";形状";也表示旋转。https://pastebin.com/57JVMN20
我读到过应该使用动态数组,这样它们就可以放在堆上,但后来我遇到了一个问题,即有人如何以上面链接的方式初始化动态数组。这似乎会让人头疼,因为我必须经历循环并具体处理每个形状?
我也很感激任何人让我在动态阵列上挑选他们的大脑——如何最好地进行它们,以及是否值得做普通阵列。
尽管我不明白你为什么要使用4D数组来存储俄罗斯方块游戏的形状,我同意波洛夫的评论,即这样的数组不应该溢出堆栈(7*4*4*4*1=448字节),所以你可能应该检查你写的其他代码。
现在,回答您关于如何管理4D(N维)动态大小阵列的问题。你可以通过两种方式做到这一点:
-
第一种方法是创建一个(N-1)维数组。如果N=2(一个表);线性化的";表的版本(普通数组),其维度等于R*C,其中R是行数,C是列数。归纳起来,您可以对N维数组做同样的事情,而不需要太多的努力。这种方法有一些缺点:
- 你需要事先知道除了一个("最新")之外的所有尺寸,并且所有尺寸都是固定的。回到N=2的例子:如果在C列和R行的表上使用此方法,则可以通过在预分配空间的末尾再分配C*sizeof(<your_array_type>)个字节来更改行数,但不能更改列数(不能不重建整个线性化数组)。此外,不同的行必须有相同数量的列C(不能为了清楚起见,在纸上绘制一个看起来像三角形的2D数组)
- 您需要小心地管理标记:您不能简单地编写
my_array[row][column]
,而是必须使用my_array[row*C + column]
访问该数组。如果N不是2,那么这个公式得到有趣
-
您可以使用N-1个指针数组。这是我最喜欢的解决方案,因为它没有以前的解决方案的任何缺点,尽管您需要管理指向…的指针。。。。指向一个类型的指针(但访问
my_array[7][4][4][4]
.时就是这样做的
溶液1
假设您想使用第一个解决方案在C中构建一个N维数组。你知道数组的每个维度的长度,直到第(N-1)个(让我们称它们为d_1,d_2,…,d_(N-1))。我们可以归纳构建:
- 我们知道如何构建动态一维数组
- 假设我们知道如何构建一个(N-1)维数组,我们证明了我们可以通过将我们现有的每个(N-1个)维数组放在一维数组中来构建一个N维数组,从而将可用维度增加1
我们还假设数组必须包含的数据类型称为T
。假设我们想创建一个数组,里面有R(N-1)维数组。为此,我们需要知道每个(N-1的)维数组的大小,所以我们需要计算它
- 对于N=1,大小仅为
sizeof(T)
- 对于N=2,大小为
d_1 * sizeof(T)
- 对于N=3,大小为
d_2 * d_1 * sizeof(T)
您可以很容易地归纳证明存储R(N-1)维数组所需的字节数是R*(d_1 * d_2 * ... * d_(n-1) * sizeof(T))
。一切都结束了。
现在,我们需要访问这个巨大的N维数组中的一个随机元素。假设我们想要访问具有标记(i_1,i_2,…,i_N)的项。为此,我们将重复归纳推理:
- 对于N=1,i_1元素的索引仅为
my_array[i_1]
- 对于N=2,(i_1,i_2)元素的索引可以通过认为每个d_1元素都有一个新的数组开始来计算,因此元素是
my_array[i_1 * d_1 + i_2]
- 对于N=3,我们可以重复相同的过程,最终得到元素
my_array[d_2 * ((i_1 * d_1) + i_2) + i_3]
依此类推。
解决方案2
第二种解决方案会浪费更多的内存,但无论是理解还是实现都更简单。
让我们坚持N=2的情况,这样我们可以更好地思考。想象一下,有一个表,将其逐行拆分,并将每一行放置在自己的内存插槽中。现在,一行是一个一维数组,要制作一个二维数组,我们只需要有一个有序的数组,其中引用了每一行。如下图所示(最后一行是第R行):
+------+
| R1 -------> [1,2,3,4]
|------|
| R2 -------> [2,4,6,8]
|------|
| R3 -------> [3,6,9,12]
|------|
| .... |
|------|
| RR -------> [R, 2*R, 3*R, 4*R]
+------+
为了做到这一点,您需要首先分配references数组(R元素长),然后遍历这个数组,并为每个条目分配一个指针,指向大小为d_1的新分配的内存区域。
我们可以很容易地将其扩展到N维。只需构建一个R维数组,对于该数组中的每个条目,分配一个大小为d_(N-1)的新一维数组,并对新创建的数组执行同样的操作,直到获得大小为d_1的数组。
请注意,只需使用表达式my_array[i_1][i_2][i_3]...[i_N]
就可以轻松地访问每个元素。
例如,假设N=3,T
是uint8_t
,并且d_1
、d_2
和d_3
在以下代码中是已知的(而不是未初始化的):
size_t d1 = 5, d2 = 7, d3 = 3;
int ***my_array;
my_array = malloc(d1 * sizeof(int**));
for(size_t x = 0; x<d1; x++){
my_array[x] = malloc(d2 * sizeof(int*));
for (size_t y = 0; y < d2; y++){
my_array[x][y] = malloc(d3 * sizeof(int));
}
}
//Accessing a random element
size_t x1 = 2, y1 = 6, z1 = 1;
my_array[x1][y1][z1] = 32;
我希望这能有所帮助。如果您有任何问题,请随时发表评论。