【问题标题】:Seg faulting with 4D arrays & initializing dynamic arrays使用 4D 数组和初始化动态数组进行分段故障
【发布时间】:2021-04-15 19:31:07
【问题描述】:

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

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

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

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

我已经读到您应该使用动态数组,以便它们最终位于堆上,但后来我遇到了一个问题,即有人如何以上面链接的方式初始化动态数组。看起来会很头疼,因为我必须遍历循环并专门处理每个形状?

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

【问题讨论】:

  • “好像会很头疼”头痛)。另一种选择是使用展平阵列。但我只会写 4 个循环。比写这个问题花费的时间更少。
  • 也shape,正如您在问题中声明的那样,它只需要` 448 个字节,一点也不应该溢出堆栈。所以你的问题出在其他地方。
  • 你是对的,当谈到形状数组时,它似乎是别的东西,但添加数组实际上会导致所有 seg 错误。可能与 SDL 以及我如何处理其中的矩形或其他东西有关。如果您有兴趣通过pastebin.com/uUjWpSSF 浏览,这里是完整的代码,我已经尝试调试它,似乎init_cells 函数是在我添加数组后导致整个事情发生故障的函数。不要介意可怕的代码,我是初学者大笑:)

标签: arrays c multidimensional-array segmentation-fault dynamic-arrays


【解决方案1】:

尽管我不明白你为什么要使用 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(您不能在纸上绘制一个看起来像三角形的 2D 数组,这只是为了让事情变得清晰)。
    • 您需要仔细管理索引:您不能简单地编写my_array[row][column],而是必须使用my_array[row*C + column] 访问该数组。如果 N 不是 2,那么这个公式就... 有趣
  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]
+------+

为此,您需要首先分配引用数组(长 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;

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-12-04
    • 2013-04-11
    • 1970-01-01
    • 2011-12-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多