使用 4D 数组分段故障并初始化动态数组

Seg faulting with 4D arrays & initializing dynamic arrays

我 运行 遇到了一个很大的问题,我目前正在用 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 (a table) 你最终会得到 table 的“线性化”版本(一个普通数组),其维度等于 R * C,其中 R 是行数,C列数。归纳地说,您可以毫不费力地对 N 维数组执行相同的操作。但是这种方法有一些缺点:

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

解决方案 1

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

  • 我们知道如何构建动态一维数组
  • 假设我们知道如何构建一个 (N-1) 维数组,我们证明我们可以通过将每个可用的 (N-1) 维数组放在一个 1-维度数组,从而将可用维度增加 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 的情况,这样我们可以更好地思考。想象一下有一个 table 并逐行拆分它并将每一行放在它自己的内存槽中。现在,一行是一个一维数组,要创建一个二维数组,我们只需要能够拥有一个包含对每一行的引用的有序数组。如下图所示(最后一行是第R行):

+------+
|  R1 -------> [1,2,3,4]
|------|
|  R2 -------> [2,4,6,8]
|------|
|  R3 -------> [3,6,9,12]
|------|
| .... |
|------|
|  RR -------> [R, 2*R, 3*R, 4*R]
+------+

为此,您需要首先分配引用数组(R 元素长),然后遍历该数组并为每个条目分配指向新分配的大小为 [=102= 的内存区域的指针].

我们可以很容易地将其扩展到 N 维。只需构建一个 R 维数组,并为该数组中的每个条目分配一个新的大小为 d_(N-1) 的一维数组,并对新创建的数组执行相同的操作,直到到达大小为 [=102 的数组=].

注意如何通过简单地使用表达式 my_array[i_1][i_2][i_3]...[i_N].

轻松访问每个元素

例如,假设 N = 3 并且 Tuint8_t 并且 d_1d_2d_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;

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