这些答案当然需要更多的深度。你对指针理解得越好,你写的代码就越少。
数组和指针是不同的,除非它们是相同的。在我的头顶上:
int a[2][2] = { 1, 2, 3, 4 };
int (* p)[2] = a;
ASSERT (p[1][1] == a[1][1]);
数组“a”的功能与指针“p”完全相同。并且编译器从每个地址中知道同样多的信息,特别是地址,以及如何计算索引地址。但请注意,数组 a 不能在运行时采用新值,而 p 可以。因此,当程序运行时,a 的“指针”方面已经消失,只剩下数组。相反,p 本身只是一个指针,它在运行时可以指向任何东西,也可以不指向任何东西。
请注意,指针声明的语法很复杂。(这就是我今天首先来到stackoverflow的原因。)但是需求很简单。您需要告诉编译器如何计算超过第一列的元素的地址。(我使用“列”作为最右边的索引。)在这种情况下,我们可能假设它需要将地址 ((2*1) + 1) 递增到索引 [1][1]。
但是,编译器知道(希望如此)还有一些您可能不知道的事情。
编译器知道两件事:1)元素是否按顺序存储在内存中,以及 2)是否真的有额外的指针数组,或者只有一个指向数组开头的指针/地址。
通常,编译时数组是按顺序存储的,与维度无关,没有额外的指针。但可以肯定的是,请检查编译器文档。因此,如果编译器允许您对 a[0][2] 进行索引,它实际上是 a[1][0] 等。但是您可以创建一个运行时数组。您可以制作您选择的任何长度的一维数组,并将它们的地址放入其他数组中,也可以选择您选择的任何长度。
而且,当然,使用其中任何一个的一个原因是因为您选择使用运行时乘法、移位或指针取消引用来索引数组。如果指针取消引用是最便宜的,您可能需要制作指针数组,因此无需进行算术运算来计算行地址。一个缺点是它需要内存来存储额外的指针。请注意,如果列长度是 2 的幂,则可以使用移位而不是乘法来计算地址。所以这可能是增加长度的一个很好的理由——编译器可以,至少在理论上,在不告诉你的情况下做到这一点!这可能取决于您选择优化速度还是空间。
任何被描述为“现代”和“强大”的架构可能会像取消引用一样快速增加,并且这些问题完全消失了——除了你的代码是否正确。