我曾经认为当我们创建像这样的多维数组时
int arr[2][3] =
{
{1, 2, 3},
{1, 2, 3}
}
内存中分配了两个单元,每个单元都占用sizeof(int) * 3,因此您可以轻松访问任何元素。例如,当访问最后三个时arr[1][2],在指针级别它将如下所示*(arr + (sizeof(int) * 3 * 1) + 2)。嗯,这样的数组正好占用 2x3 内存。
但后来我在指针层面看到了这样的公式arr[1][2] => *(*(arr + 1) + 2),得出的结论是,多维数组在内存中存储如下(这里我将这样表示内存0[5],其中0是单元格地址[5], 是存储在单元格内部的值): 0[адрес 2], 1[адрес 5], 2[1], 3[2], 4[3], 5[1], 6[2], 7[3]. 通过这样的存储,您可以轻松理解为什么*(*(*(arr + i) + j) + k)在每个级别的该公式中都会发生取消引用。然而,采用这种存储方式,同一个数组已经占用了2+2x3。
既然存储的是多维数组,如果我描述的第二种方法是正确的,那么它比第一种方法有什么优势呢?
不,对于数组来说,内存中只存储它的元素,而不存储指针。
int arr[2][3]它只占用sizeof(int) * 2 * 3一个字节的空间。但同时,这个公式
arr[1][2] => *(*(arr + 1) + 2)也是正确的。诀窍在于必要的指针是动态计算的。在许多情况下,数组会自动转换为指向其第一个元素的指针,包括在本例中。生成的指针是动态计算的,并且不会永久存储在内存中。
另外,您需要了解多维数组只是常规(一维)数组,但其元素类型也是数组。对他们没有特殊的规则。
让我们考虑一下
*(*(arr + i) + j)。+首先将数组arr(类型int[2][3])转换为指向其第一个元素的指针(就像您所做的那样&arr[0])类型int (*)[3](“指向 3 个整数的数组的指针”)。然后
+ i将指针移至i * sizeof(тип-элемента)一个字节,其中元素类型为int[3], asizeof(int[3]) == sizeof(int) * 3。然后
*它取消引用该指针,并且结果具有一个类型int[3](一个 3 个整数的数组)。然后对第二个索引重复同样的事情。
+将数组转换为类型的指针int *,等等。我希望您理解数组的元素在内存中是连续的,因此
sizeof(int)*3您也可以说“6 个单元”,而不是“两个单元sizeof(int)”。它只是连续 6 个整数。