以下是基本规则:
对于任何类型T
,您可以拥有以下任意一种:
T *p
T *a[N]
T (*a)[N]
T *f()
T (*f)()
后缀运算符“[]”和“()”的优先级高于一元运算符“*”,因此像“*p[i]”这样的表达式被解析为“*(p[i])”。如果您想要索引到“p”所指向的内容,则需要显式地将“*”运算符与“p”分组,或者写成“(*p)[i]”。这个优先级规则适用于表达式和声明。
除非它是“sizeof”、“_Alignof”或一元运算符“&”的操作数,或者是用于在声明中初始化字符数组的字符串字面量,否则类型为“N元素数组T”的表达式将被转换(“衰减”)为类型为“指向T的指针”的表达式,并且表达式的值将是数组的第一个元素的地址。因此,给定声明:
int a[4] = {10, 20, 30, 40}
以下所有内容都是正确的:
Expression Type Decays to Equivalent value
a int [4] int * &a[0]
&a int (*)[4] n/a &a[0]
*a int n/a a[0]
表达式
a
的类型为"4元素数组
int
" (
int [4]
)。由于
a
不是
sizeof
、
_Alignof
或一元
&
运算符的操作数,因此该表达式"衰减"为"指向
int
的指针",该表达式的值是第一个元素的地址。该表达式的结果与
&a[0]
完全等价。
表达式
&a
的类型为"指向4元素数组
int
的指针"(
int (*)[4]
)。在这种情况下,
a
是一元
&
运算符的操作数,因此衰减规则不适用。
所有表达式
a
、
&a
和
&a[0]
都产生相同的值(即
a
的第一个元素的地址),但表达式的类型不同(这可能会影响值的表示方式)。
a
和
&a[0]
的类型是
int *
,而
&a
的类型是
int (*)[4]
。
类型对于指针算术等事情很重要。假设以下声明:
int a[4] = {0, 1, 2, 3};
int *p = a;
int (*ap)[4] = &a;
变量 p
和 ap
最初指向同一个地址。然而,表达式 p + 1
将产生紧随 p
所指向的下一个 int
对象的地址(即 &a[1]
),而 ap + 1
将产生下一个由 a
指向的 4 元素 int 数组 的地址。
这正是数组下标运算的工作原理 - 表达式 a[i]
被计算为 *(a + i)
。给定起始地址 a
,偏移 i
个对象(不是字节)并解引用结果。
这就是为什么在某些赋值中会出现错误的原因 - 类型 int *
和 int (*)[4]
不是 兼容的。首先,它们不必以相同的方式表示(尽管在您可能使用的任何系统上它们都将是),并且在使用指针算术时行为不同。
指向不同类型的指针本身就是不同的类型,通常不能互换使用。
几乎所有类型都可以,但函数不能返回数组或函数类型,也不能有函数类型的数组。因此,对于
T (*a)[N]
,
T
不能是函数类型;对于
T (*f)()
,
T
不能是函数或数组类型。
printf("p = %p\np++ = %p\n", p, p+1);
),你会发现该地址比 'p' 的地址大 **4 * sizeof(int)**。 - mar