(*b)[0]与*b[0] - 数组和指针

4

我正在处理一些玩具问题,以了解C语言中指针和数组之间的区别,并且我遇到了一些我还无法理解或找到答案的问题。

我有以下程序:

#include <stdio.h>

int main()
{
    int a[3][3] = {{1, 2, 3}, {4, 5,6}, {7, 8, 9}};
    int (*b)[3];

    b = a;

    printf("%d %d %d\n", (*b)[0], (*b)[1], (*b)[2]);
    printf("%d %d %d\n", *b[0], *b[1], *b[2]);

    b++;
    printf("%d %d %d\n", (*b)[0], (*b)[1], (*b)[2]);

    b++;
    printf("%d %d %d\n", (*b)[0], (*b)[1], (*b)[2]);

    return 0;
}

当我编译并运行它时,我得到了这个结果:
me@mac: clang -g q.c -o q.o                                                                     
me@mac: ./q.o
1 2 3
1 4 7
4 5 6
7 8 9

我的问题是(*b)[0]*b[0]有什么区别?前者似乎是int *类型,而后者是int *[3]类型。如何解释运算符*[]()以理解这个问题?
6个回答

3

int (*b)[3];是一个数组指针,它是一种特殊的指针,可以指向整个数组,而不仅仅是第一个元素。不要与int *b[3]混淆,后者是一个包含3个指针的数组,在您的代码中不存在。

b=a将b设置为指向2D数组a中的第一个元素,即1D数组。

当您解引用数组指针时,您会得到该数组。当数组用于表达式中时,它会“衰变”为指向第一个元素的指针,这解释了第一行printf。

如果您写*b[0],那么[]具有比*更高的运算符优先级,因此它的意思是“给我数组号0,它会衰变为指向第一个元素的指针,给我该元素的内容”。

对于任何指针类型,包括数组指针,b的指针增量遵循指针算术规则:增加地址,使指针指向内存中指定类型的下一个相邻项。也就是说,增加sizeof(*b)字节的地址,这在本例中将是3*sizeof(int)


啊,我写运算符的时候应该考虑到运算符优先级。你的答案为我解决了疑惑。 - Idr

2
这仅适用于二维数组,而不是任意的随机指针。
当使用(* b) [1]时,先进行解除引用,然后进行索引,因此它实际上表示b [0] [1],您正在从第一个数组中获取值。
如果使用* b [1],则与*(b [1])相同。索引运算符将首先处理,然后再进行解除引用,因此您会得到b [1] [0]

1
我的问题是 (*b)[0] 和 *b[0] 之间的区别是什么?
int (*b)[3]; 是一个指向大小为 3 的一维数组的指针,在这种情况下,因为有 (),所以首先对指针进行解引用,然后进行索引访问。
*b[3] 是一个包含 3 个指针的数组,在这种情况下,由于 [],首先进行索引访问。
因此,当你进行...
printf("%d %d %d\n", (*b)[0], (*b)[1], (*b)[2]);

printf("%d %d %d\n", *b[0], *b[1], *b[2]);

在第一个printf中,它打印了第一个一维数组的元素,在第二个printf中,它指向每行的第一个元素。
在第一个情况中,经过b++后,它指向第二个数组,并且第二个printf指向每行的第二个元素。

0

b 是一个指向包含 3int 的数组的指针。在赋值语句中

b = a;  

a将会衰变为指向其第一个元素的指针。由于a是一个数组的数组,它的第一个元素是一个包含3int的数组。因此,上述语句等价于

b = &a[0];

因此,b 指向数组 a[0]。可以使用索引 a[0][i] 访问数组 a[0] 的元素。在指针算术中,等价于 *(*(a + 0) + i) = *(*(a) + i) = *(*a + i) = (*a)[i]
在表达式 (*a)[i] 中,a 转换后的类型为 int (*)[3],这也是 b 的类型。因此,数组 a[0] 也可以通过使用 (*b)[i] 来访问。
所以,该语句

printf("%d %d %d\n", (*b)[0], (*b)[1], (*b)[2]);  

等同于

printf("%d %d %d\n", (*a)[0], (*a)[1], (*a)[2]);   

或者

printf("%d %d %d\n", a[0][0], a[0][1], a[0][2]);   

现在看一下*b [0]。根据运算符优先级规则,*b [0]等同于*(b [0])。简单地说,b [0]表示数组a [0],如上所述,在表达式中使用时,数组转换为指向其第一个元素的指针,有一些例外情况,b [0]实际上是指向元素a [0] [0]的指针,在表达式*(b [0])中。 *()中取消引用此指针,并给出&a [0] [0]中存在的值。同样,*b [1]*b [2]分别在地址&a [1] [0]&a [2] [0]处给出元素。
因此,该语句

printf("%d %d %d\n", *b[0], *b[1], *b[2]);  

等同于

printf("%d %d %d\n", *a[0], *a[1], *a[2]);    

或者

printf("%d %d %d\n", &a[0][0], &a[0][1], &[0][2]);

这导致一个结论,即(*b)[0]*b[0]是相同类型的,都是int


0
有点晚了,但是答案似乎漏掉了一些重要的点。

(*b)[0] 和 *b[0] 有什么区别?

假设 b 是一个指向包含3个整数的数组的指针,并且指向 a
int (*b)[3] = a; // int a[3][3]

为了避免一些混淆,让我们假设 ab 是。
int a[X][Y];     // in your example, X=Y=3
int (*b)[Y] = a; // this line probably gives some clue about what b is

这是一个优先级问题。 [] 的优先级高于 *,这意味着

  • 如果你不把括号放在 *b[0] 上,b[0] 将首先被计算并产生一个 int [Y],即 [X][Y] 数组的第一个 [Y] 数组,然后应用 * 并衰减(解除引用)为一个 int,即该数组中的第一个 Yint
    结果(或目标)是 a[0][0] 中的 int

  • 有了括号,(*b)[0]*b 首先被计算,产生一个 int [Y],第一个,然后 [0] 给出该 [Y] 的第一个 int
    结果(或目标)是 a[0][0] 中的 int。再一次...真的吗?

但是这两个[0]并不是同一件事情!混淆的原因在于*[0]都会返回第一个元素,但是在两种情况下,求值顺序是不同的。


0

(*b)[0] 先解引用指针,然后进行索引访问。 它等同于 *((*b)+(0))

*b[0] 先进行索引访问,然后解引用指针。 它等同于 **((b)+(0))

加零基本上没有意义,所以它们都等同于类型为 int**b,因此几乎没有区别。


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