指向多维数组的指针数组

4
我有一些二维数组,例如:
int shape1[3][5] =  {1,0,0,
             1,0,0,
             1,0,0,
             1,0,0,
             1,0,0};
int shape2[3][5] =  {0,0,0,
             0,0,0,
             0,1,1,
             1,1,0,
             0,1,0};

等等。

我怎样才能创建一个指向它们的指针数组呢?

我尝试了以下代码,但是它们不起作用(警告:从不兼容的指针类型进行初始化):

int *shapes[]=  {&shape1,&shape2};

int *shapes[]=  {shape1,shape2};

int **shapes[]= {&shape1,shape2};

需要帮忙吗?

3个回答

6

我相信我刚刚证实了我写的是正确的。以下内容可以按预期工作:

#include <stdio.h>

int main(int argc, char **argv) {

int shape1[5][3] =  {1,0,0,
                 1,0,0,
                 1,0,0,
                 1,0,0,
                 1,0,0};

int shape2[5][3] =  {0,0,0,
                 0,0,0,
                 0,1,1,
                 1,1,0,
                 0,1,0};

typedef int (*shapes_p)[3];
shapes_p shapes[2] = { shape1, shape2 };

shapes[0][1][0] = 5;
shapes[1][1][0] = 5;

printf("shape1[1][0] == %d\n", shape1[1][0]);
printf("shape2[1][0] == %d\n", shape2[1][0]);

}

需要翻译的内容如下:

需要记住的是,shape1shape2 的类型实际上是:

int *shape1[5];

在内存中,您拥有 3 个相邻的包含 5 个 int 的数组。但实际类型是指向包含 5 个 int 的数组的指针。当您编写以下代码时:

shape1[1][2] = 1;

您要求编译器索引到第二个 int[5] 数组,然后访问该数组的第三个元素。编译器实际上执行的是指向底层类型 int[5] 的指针算术运算。您可以使用以下代码执行相同的操作:

int *p = shapes1[0];
p+7 = 1;  // same as shape1[1][2] = 1;

那么,如果你想要一个指向int *[5]的指针数组,你需要这样做:
typedef int (*shapes_p)[5];
shapes_p shapes[2];

-1 抱歉--shape1的类型实际上是“int shape1 [3] [5]”,只是在表达式中使用名称shape1会产生类型为“int *[5]”(即指向数组第一个元素的指针)的值。 - j_random_hacker
1
也许我漏掉了什么,但我检查了我的解决方案,它按预期工作。我在帖子开头添加了一个完整的可编译示例。 - Robert S. Barnes
非常抱歉,Robert,你是完全正确的。 您可以使用“T ptr”来访问T的一维数组,所以您应该使用“T( ptr)[3]”来访问行宽为3的T的二维数组。 +1。 - j_random_hacker
我想澄清这个问题的一个基本方面。不确定它是否应该是一个新的问题,所以我会从这里开始。这种方法严格为指针(当然最终是它们的数组)分配内存,并且绝不会为底层的指针和int数组分配内存,正确吗?所以,shape1 占用的空间量没有被重复?或者换句话说,由 int (*shapes_p)[5] 定义的总体类型和分配给它的相应内存仍然只有一个指针? - GG2

3

更新:修复了类型错误。感谢j_radom_hacker提醒!

[编辑:实际上这里的类型不正确--请参见Robert S. Barnes的答案以获取正确的类型。]

首先确定shape1shape2的类型:

typedef int (*shape_array_t)[5];

现在使用这个:
shape_array_t sat[] = { shape1, shape2 };

嘿,我打错字了——对不起 :P - dirkgently
@j_random_hacker:我想在你的评论之前就已经修复了它;-) - dirkgently
实际上,我仔细阅读了Robert Barnes的答案,他是正确的——我们不应该在sat的类型中包含外部下标("[3]")。按照现有的情况,要使用你(或者我最初)的代码访问单个int,你需要写例如"sat[1][0][2][4]"——第二个下标"[0]"需要选择第一个3x5数组! - j_random_hacker
@dirkgently:我已经恶意编辑了你的答案,以便我可以将其评为-1...请不要把它当作个人攻击,但现在很明显这个被标记为“已接受”的答案是错误的。(我的答案也是/曾经是错误的,你可能应该给它点踩。) - j_random_hacker
我刚刚修正了答案。我不想浪费时间去点踩,但我会给Robert一个赞(+1)。 - dirkgently

3

首先,第一个数组边界是指最外层数组的维度,因此您应该将shape1声明为:

int shape1[5][3] =  {1,0,0,
                     1,0,0,
                     1,0,0,
                     1,0,0,
                     1,0,0};

同样的,shape2也是如此。

[编辑:我已经更改了下面的shapes类型,以对应Robert Barnes的答案 -- 我们不希望最外层的下标包含在这个类型中!]

你需要的有点奇怪的类型名是:

int (*shapes[])[3] = { shape1, shape2 };

这使得可以通过使用行4、列1的元素来访问shape2中的元素。
shapes[1][3][0]

子表达式及其C类型的分解:

shapes            // has type "int (*x[2])[3]" (decays to "(**x)[3]")
shapes[1]         // has type "int (*x)[3]"
shapes[1][3]      // has type "int x[3]" (decays to "int *x")
shapes[1][3][0]   // has type "int x"

(请注意,上述类型中包含一个虚拟的“x”以使它们更清晰 - 实际上,此标识符不是类型的一部分。)
解码C/C++类型的经验法则是:“从变量名开始,向右读取,当遇到右括号时向左读取。” 因此,shapes 的解码类型名称为:
“指向包含3个整数数组的指针数组”。
通常最好使用typedef来处理这些复杂的类型,正如dirkgently建议的那样

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接