简短回答
如果已经声明了userMask变量为
int *userMask[3][4];
那么userMask
的类型为int*[3][4]
。它是一个指向int的2D数组。外部维度的大小为3,内部维度的大小为4。实际上,这只是一个由3个元素组成的1D数组,其中每个元素的类型都是另一个由4个元素组成的1D数组,其元素类型是int*
。
步骤解释
因此,如果您执行以下操作:
userMask[2][maskElement][user]
然后,基本上你使用前两个索引从2D数组中选择特定的指针:
int * p = userMask[2][maskElement];
然后您可以通过执行 int
类型的偏移量来选择指针上的某个位置,如下所示:
p[user]
现在所有的代码都在userMask[2][maskElement][user]
中。
合法的C代码
为了一步步地使用合法的C代码实现它(如果您还不理解以下内容也不用担心):
int * userMask[3][4] = { { 0 } };
int ** pa = userMask[2];
int * pi = pa[maskElement];
int i = pi[user];
assert(i == userMask[2][maskElement][user]);
数组和指针的区别
我认为我向您展示了一些重要的内容。上面的数组不包含指向数组的指针。让我们看看它们的行为有多不同,这是许多 C 程序员没有预料到的:
int array[5][4][3];
int (*parray)[3] = array[0];
int ** pint = (int**) array[0];
现在,如果我们执行
parray[1]
和
pint[1]
会发生什么呢?前者将使指针parray前进
sizeof(int[3])
字节(即
3 * sizeof(int)
),后者只会前进
sizeof( int* )
字节。所以虽然第一个可以得到正确的数组值
array[0][1]
,但第二个将给出
( char * )array[0] + sizeof( int* )
,这不是我们想要的结果。但获取错误的偏移量还不是全部问题。由于它不知道正在访问一个数组,它会尝试将
pint[1]
解释为一个
int*
类型。假设你的数组初始化为
0x00
。那么接下来它将基于地址0x00进行下一个索引步骤(例如执行
pint[1][0]
)。哦,不好了——完全未定义的行为!因此强调这种差异非常重要。
结论
这比你要求的更多,但我认为这些细节非常重要。特别是如果你想将2D数组传递给函数,那么这些知识真的很有用。