指针和多维数组

3

可能是重复问题:
如何在C++中使用数组?
二维数组是双指针吗?
二维数组和指针

我知道这是一个非常基本的问题,但无论我怎样搜索都无法搞清楚。这就是为什么我要在这里发布它的原因。 在c++中考虑声明int x [10];

这是一个一维数组,x是基指针,即它包含数组第一个元素的地址。所以x给出了那个地址,*x给出了第一个元素。

类似地,在声明中

 int x[10][20];

这里的变量 x 是哪种类型?当我执行时。
 int **z = x;

编译器提示无法将 int (*)[20] 转换为 int **。为什么 cout<<x;cout<<*x; 给出相同的值?同时,如果我声明一个指针数组,如下:

 int *p[10];

那么,xp(它们的类型)之间有区别吗?因为当一个人声明int x[10]int *p时,将x赋给p是有效的,但在二维数组的情况下却不是这样。为什么?请问有人能为我解释一下或提供一个好的资源材料吗?

2
所有这些问题都可以通过阅读C语言中关于数组和指针的常见问题解答得到答案。 - Oliver Charlesworth
你也可以像这样从二维数组中检索元素 (x + 20(row) + col),因此 x 是一个多维数组,仍然会给出第一个值的指针。 - user1084113
3个回答

5

数组和指针并不是同一种东西。在C和C++中,多维数组只是“数组的数组”,没有涉及指针。

int x[10][20];

这是一个包含10个数组,每个数组有20个元素的数组。如果你在一个上下文中使用x,它会衰减为指向其第一个元素的指针,那么你最终得到的是一个指向这20个元素数组之一的指针 - 这就是你的int (*) [20]。注意,这样的东西不是指向指针的指针,因此无法进行转换。

int *p[10];

这是一个由10个指针组成的数组,因此它与 x 不同。

特别是,您可能会遇到麻烦,因为您似乎认为数组和指针是相同的东西——您的问题说:

这是一个一维数组,x 是基指针,即它包含数组第一个元素的地址。 因此,x 给我该地址,*x 给我第一个元素。

这并不正确。 一维数组 x 就是一个数组,在某些情况下,数组会退化为指向其第一个元素的指针。

阅读FAQ了解有关此主题的所有内容。


谢谢您的回复。我现在比刚开始时更清楚了...但我仍然不明白**x是如何工作的。在这里,我们几乎将x用作指向第一个元素的指针的指针,对吗?因为**x给出数组的第一个元素的值。 - newbie
@新手,你在任何示例中都不能使用**x - Carl Norum
#include<iostream> using namespace std; main() { int x[10][20]; x[0][0]=11; cout<<**x<<endl; return 0; } 我的编译器输出11。 - newbie

5

多维数组只是一种语法糖。在幕后,这只是一个长度为10 * 20的一维数组,存储的是int类型的数据。当您访问x[5][6]元素时,编译器会生成相当于x[5 * 20 + 6]的代码;


1
这是正确的,但并没有回答问题。 - Oliver Charlesworth
@OliCharlesworth: 我觉得OP不确定一维数组和多维数组之间的物理差异,并希望澄清。由于他直接使用数组内存,了解数组在内存中实际布局可能会有所帮助。 - alekop
访问矩阵x中的第5行第6列元素,需要使用下标表达式x[5 * 20 + 6]。其中,矩阵x有10行20列,按行主序存储。 - Ritwik
@Ritwik 你是对的。我更新了答案。谢谢。 - alekop
如果那么简单,它就能工作。但事实并非如此。 - MarcusJ
有没有人想在C语言中尝试这个等价性,x[5][6]((int*)x)[5 * 20 + 6](转换为'int'类型的'数组')是相同的。 - fordcars

2
以下是它的实际工作原理,可查看代码和注释:

#include <stdio.h>

int x[3][5] =
{
  {  1,  2,  3,  4,  5 },
  {  6,  7,  8,  9, 10 },
  { 11, 12, 13, 14, 15 }
};

int (*pArr35)[3][5] = &x;
// &x is a pointer to an array of 3 arrays of 5 ints.

int (*pArr5a)[5] = x;
// x decays from an array of arrays of 5 ints to
// a pointer to an array of 5 ints,
// x is a pointer to an array of 5 ints.

int (*pArr5b)[5] = &x[0];
// &x[0] is a pointer to 0th element of x,
// x[0] is an array of 5 ints,
// &x[0] is a pointer to an array of 5 ints.

int *pInta = x[0];
// x[0] is 0th element of x,
// x[0] is an array of 5 ints,
// x[0] decays from an array of 5 ints to
// a pointer to an int.

int *pIntb = *x;
// x decays from an array of arrays of 5 ints to
// a pointer to an array of 5 ints,
// x is a pointer to an array of 5 ints,
// *x is an array of 5 ints,
// *x decays from an array of 5 ints to
// a pointer to an int.

int *pIntc = &x[0][0];
// x[0][0] is 0th element of x[0],
// where x[0] is an array of 5 ints,
// x[0][0] is an int,
// &x[0][0] is a pointer to an int.

int main(void)
{
  printf("&x=%p x=%p &x[0]=%p x[0]=%p *x=%p &x[0][0]=%p\n",
         pArr35, pArr5a, pArr5b, pInta, pIntb, pIntc);
  return 0;
}

样例输出:

&x=0040805c x=0040805c &x[0]=0040805c x[0]=0040805c *x=0040805c &x[0][0]=0040805c

所有的指针结果在数值上是相同的,因为我明确或隐式地使用了索引0,并且数组是连续的,它们的第一个(也就是0号)元素总是在数组中最低的地址处。所以,即使有3种不同的指针类型,它们都有效地指向x[0][0],即等于1的元素。

这种将数组衰减为指针的特性是C和C++非常重要的特性,尽管它不容易立即理解。

当我们传递指向数组的指针时,它让我们编写更紧凑的代码,我们可以只写数组的名称而不是取其第一个元素的地址:

char str1[] = { 's', 't', 'r', '1', '\0' };
char str2[] = "str2";
printf("%s %s %s %s\n", &str1[0], str1, &str2[0], str2);

输出:

str1 str1 str2 str2

它还让我们可以做疯狂的事情:

int y[3] = { 10, 20, 30 };
printf("%d %d %d %d\n", y[2], *(y+2), *(2+y), 2[y]);

输出:

30 30 30 30

这是因为在数组和指针中,a[b] 等同于 *(a+b)


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