使用 4D 数组进行分段错误并初始化动态数组

问题描述

我目前用 C 编写的俄罗斯方块程序遇到了一个大问题。 我正在尝试使用 4D 多维数组,例如

uint8_t shape[7][4][4][4]

但是当我尝试这样做时,我一直遇到段错误,我已经阅读了,似乎我正在用这种数组耗尽所有堆栈内存(我所做的只是用0 和 1 来描绘形状,所以我不会输入一个高得离谱的数字或其他东西)。

这是它的一个版本(在 pastebin 上,因为你可以想象它非常丑陋和冗长)。 如果我将数组变小,它似乎可以工作,但我试图避免绕过它,因为理论上每个“形状”也代表一个旋转。 https://pastebin.com/57JVMN20

我已经读到您应该使用动态数组,以便它们最终出现在堆上,但后来我遇到了如何以上面链接的方式初始化动态数组的问题。似乎很头疼,因为我必须通过循环并专门处理每个形状?

我也很感激任何人让我在动态数组上挑选他们的大脑,如何最好地处理它们,以及是否值得做普通数组。

解决方法

尽管我不明白你为什么要使用 4D 数组来存储俄罗斯方块游戏的形状,但我同意 bolov 的评论,即这样的数组不应溢出堆栈(7*4*4*4*1 = 448字节),因此您可能应该检查您编写的其他代码。

现在,关于如何管理 4D(N 维)动态大小的数组的问题。您可以通过两种方式执行此操作:

  1. 第一种方法是创建一个 (N-1) 维数组。如果 N = 2(一个表格),您最终会得到表格的“线性化”版本(一个普通数组),其维度等于 R * C,其中 R 是行数,C 是列数。归纳地说,您可以毫不费力地对 N 维数组做同样的事情。但是这种方法有一些缺点:

    • 您需要事先知道除一个(“最新”)之外的所有维度,并且所有维度都是固定的。回到 N = 2 的例子:如果你在一个 C 列和 R 行的表上使用这个方法,你可以通过在预分配空间的末尾分配 C * sizeof() 更多字节来改变行数,但不是列数(不是没有重建整个线性化数组)。此外,不同的行必须具有相同的列数 C(你不能有一个在纸上绘制时看起来像三角形的二维数组,只是为了让事情清楚)。
    • 您需要仔细管理索引:您不能简单地编写 my_array[row][column],而必须使用 my_array[row*C + column] 访问该数组。如果 N 不是 2,那么这个公式会变得……有趣
  2. 您可以使用 N-1 个指针数组。这是我最喜欢的解决方案,因为它没有前一个解决方案的任何缺点,尽管您需要管理指向指针的指针指向 .... 指向类型的指针(但这就是您在访问 {{1 }}。

解决方案 1

假设您想使用第一个解决方案在 C 中构建一个 N 维数组。 您知道数组的每个维度的长度,直到第 (N-1) 次(我们称它们为 d_1、d_2、...、d_(N-1))。我们可以归纳地构建它:

  • 我们知道如何构建一个动态的一维数组
  • 假设我们知道如何构建一个 (N-1) 维数组,我们展示了我们可以通过将我们可用的每个 (N-1) 维数组放入一维数组中来构建一个 N 维数组,从而将可用维度增加 1。

我们还假设数组必须保存的数据类型称为 my_array[7][4][4][4]。 假设我们要创建一个包含 R (N-1) 维数组的数组。为此,我们需要知道每个 (N-1) 维数组的大小,因此我们需要对其进行计算。

  • 对于 N = 1,大小仅为 T
  • 对于 N = 2,大小为 sizeof(T)
  • 对于 N = 3,大小为 d_1 * sizeof(T)

你可以很容易地归纳证明存储 R (N-1) 维数组所需的字节数是 d_2 * d_1 * sizeof(T)。大功告成。

现在,我们需要访问这个庞大的 N 维数组中的随机元素。假设我们要访问具有索引 (i_1,i_2,...,i_N) 的项目。为此,我们将重复归纳推理:

  • 对于 N = 1,i_1 元素的索引就是 R*(d_1 * d_2 * ... * d_(n-1) * sizeof(T))
  • 对于N = 2,(i_1,i_2)元素的索引可以通过认为每个d_1个元素,一个新数组开始,所以元素为my_array[i_1]
  • 对于 N = 3,我们可以重复相同的过程,最终得到元素 my_array[i_1 * d_1 + i_2]

等等。

解决方案 2

第二种解决方案浪费了更多的内存,但它更直接,易于理解和实现。

让我们坚持 N = 2 的情况,以便我们可以更好地思考。想象一下有一个表,将它逐行拆分,并将每一行放置在自己的内存插槽中。现在,一行是一个一维数组,要创建一个二维数组,我们只需要能够拥有一个包含对每一行的引用的有序数组。如下图所示(最后一行是第 R 行):

my_array[d_2 * ((i_1 * d_1) + i_2) + i_3]

为了做到这一点,您需要首先分配引用数组(R 个元素长),然后遍历该数组并将指向新分配的大小为 d_1 的内存区域的指针分配给每个条目。

我们可以轻松地将其扩展为 N 维。简单地构建一个 R 维数组,并为该数组中的每个条目分配一个大小为 d_(N-1) 的新一维数组,并对新创建的数组执行相同操作,直到获得大小为 d_1 的数组。

请注意如何通过简单地使用表达式 +------+ | R1 -------> [1,2,3,4] |------| | R2 -------> [2,4,6,8] |------| | R3 -------> [3,9,12] |------| | .... | |------| | RR -------> [R,2*R,3*R,4*R] +------+ 轻松访问每个元素。

例如,假设 N = 3 且 my_array[i_1][i_2][i_3]...[i_N]T 并且 uint8_td_1d_2 在以下代码:

d_3

我希望这会有所帮助。如果您有任何问题,请随时发表评论。