(arr + 2)相当于*(arr + 2),为什么?

9

我正在学习如何使用指针显示二维数组的元素。以下是我尝试的代码:

#include<stdio.h>  

int main(){

    int arr[3][2] = {  

    {7, 8},
    {6,3},
    {3,4}
    };

    printf("%u\n", (arr + 2));
    printf("%u\n", *(arr + 2)); 
}

输出:

6487616
6487616

我希望 *(arr + 2) 的输出结果是 3。这和 (arr + 2) 有什么关系呢?


3
由于它是一个二维数组,所以您需要解引用两次:*(*(arr+2)) - Govind Parmar
4
在语言C11中,arr[2]*(arr+2)的定义是相同的(参见C11§6.5.2.1数组下标运算符第2段)。由于您打印的是一个地址而不是数组元素的值,因此会产生奇怪的结果。这是正式未定义的行为;您向printf()传递了一个地址,但告诉它要处理一个无符号整数。在32位平台上可能还好,但在64位平台上结果就完全不可靠了。 - Jonathan Leffler
1
从技术上讲,由于“%u”不是用于打印指针,因此您正在获得UB。您必须使用“%zu”。 - phuclv
7
你说的对,使用 %u 是未定义行为,但是使用 %zu 也是(用于打印 size_t)。你应该使用 %p 并将指针转换为 void *,或者使用 <inttypes.h> 中的 PRIuPTR(或 PRIXPTR 用于大写十六进制)加上一个 uintptr_t 类型的转换。 - Jonathan Leffler
1
@phuclv:False。%zu需要一个size_t参数。任何不兼容的类型都会触发未定义行为。指针参数也会触发未定义行为。 - AnT stands with Russia
显示剩余5条评论
3个回答

14

一个2D数组实际上是一个数组的数组。

表达式arr + 2的类型为int (*)[2],而*(arr + 2)的类型为int [2]。打印前者时,您有一个指针,因此打印指针的值。在后一种情况下,您有一个数组,它被衰减为指向第一个元素的指针。所以,*(arr + 2)衰减为arr + 2,与第一个表达式相同。

进一步详细说明arr + 2arr的类型为int [3][2]。当您将整数值添加到它时,它会衰减为指向第一个成员的指针,因此arr会衰减为类型int (*)[2]arr + 2也具有该类型,并指向包含{ 3, 4 }的子数组。

还要注意,应使用%p格式说明符打印指针,指针必须转换为void *,否则将调用未定义的行为。在这种情况下,您“幸运”地打印了相同的内容。

要获得您期望的输出3,您需要再次进行解引用:

*(*(arr + 2))

*(*(arr + 2)); throws [Error] expected ')' before ';' token but *(*(arr + 1) + 1)); prints 3 - dave
1
@dave 对我来说 *(*(arr + 2)) 正常工作。你可能忘记了在 printf 调用中加上一个闭合括号。此外,*(*(arr + 1) + 1)) 选择了另一个数组元素,它恰好具有您尝试打印的值。arr[2][0]arr[1][1] 都包含值 3。 - dbush

3
arr是一个包含int数组的数组。几乎在任何使用中,数组都会转换为指向其第一个元素的指针。因此,arr会被转换为指向int数组的指针。
好了,arr被转换为指向int数组的指针,所以(arr+2)也是同一类型的指针,即指向int数组的指针。
现在,*(arr+2)是指向(arr+2)指向的内容,也就是一个int数组。
既然它是一个int数组,它就会被转换为指向其第一个元素的指针。所以*(arr+2)会被转换为指向int的指针。请注意,它不是一个int,并且不可能等于3
那么为什么(arr+2)*(arr+2)显示相同?它们分别是指向数组和指向其第一个元素的指针。虽然这些指针是不同类型的,但它们表示相同的地址,因为任何数组的地址都与其第一个元素的地址相同。

1

arr是指向类型为int[2]的第一个数组的指针。(arr + 2)是指向第三个这样的数组的指针。
*(arr + 2)是指向(arr + 2)数组的第一个元素的指针。
由于它们都指向同一位置,因此两者具有相同的地址。唯一的区别在于它们的类型。(arr+2)的类型为int(*)[2],而*(arr + 2)的类型为int *


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