3 除非它是 sizeof 运算符、 _Alignof 运算符或一元 & 运算符的操作数,或者是用于初始化的字符串字面量,类型为 “array of type ” 的表达式被转换为类型为 “pointer to type ” 的表达式,它指向数组对象的初始元素,而不是左值。如果数组对象具有寄存器存储类,则行为未定义。
让我们看看以下声明:
int arr[10] = {0,1,2,3,4,5,6,7,8,9};
int *parr = arr;
arr 是 int 的 10 元素数组;它指的是一个足够大的连续内存块以存储 10 int 值。第二个声明中的 表达式arr 是数组类型,但由于它不是 & 或 sizeof 的操作数并且它不是字符串, 表达式 的类型变为“指向 int 的指针”,值为第一个元素的地址,或 &arr[0] 。
parr 是指向int的指针;它指的是一块大到足以容纳单个 int 对象的地址的内存块。如上所述,它被初始化为指向 arr 中的第一个元素。
类型对于 sizeof 和 & ; sizeof arr == 10 * sizeof (int) , which in this case is 20, whereas sizeof parr == sizeof (int *) , which in this case is 4. Similarly, the type of the expression &arr is int (*)[10] ,或指向 int 的类型是 &parrint ** 的指针,或指向 int 的指针 --- 。
请注意,表达式 arr 和 &arr 将产生相同的 _值_( arr 中第一个元素的地址),但表达式的类型不同( int * 和 int (*)[10] 分别)。这在使用指针算法时会有所不同。例如,给定:
int arr[10] = {0,1,2,3,4,5,6,7,8,9};
int *p = arr;
int (*ap)[10] = &arr;
printf("before: arr = %p, p = %p, ap = %p\n", (void *) arr, (void *) p, (void *) ap);
p++;
ap++;
printf("after: arr = %p, p = %p, ap = %p\n", (void *) arr, (void *) p, (void *) ap);
“之前”行应该为所有三个表达式打印相同的值(在我们的假设地图中, 0x10008000 )。 The “after” line should show three different values: 0x10008000 , 0x10008002 (base plus sizeof (int) ), and 0x10008014 (base plus sizeof (int [10]) )。
现在让我们回到上面的第二段:在大多数情况下,数组 表达式 被转换为指针类型。我们来看下标表达式 arr[i] 。由于表达式 arr 没有作为 sizeof 或 & 的操作数出现,并且由于它不是用于初始化另一个数组的字符串文字,类型从“ int ”的10元素数组转换为“指向 int 的指针”,并且正在对该 指针 值应用下标操作。实际上,当您查看 C 语言定义时,您会看到以下语言:
int foo(int *p, size_t size)
{
int sum = 0;
int i;
for (i = 0; i < size; i++)
{
sum += p[i];
}
return sum;
}
int main(void)
{
int arr[10] = {0,1,2,3,4,5,6,7,8,9};
int result = foo(arr, sizeof arr / sizeof arr[0]);
...
}
以它的方式工作。 main is dealing with an array of int , whereas foo is dealing with a pointer to int , yet both are able to use the下标运算符 ,就好像 它们都在处理数组类型一样。
It also means array subscripting is commutative : assuming a is an array expression and i is an integer expression, a[i] and i[a] are both有效的表达式,并且两者都将产生相同的值。
让我们先把重要的东西弄清楚: 数组不是指针。数组类型和指针类型是 _完全不同的东西_,编译器的处理方式也不同。
产生混淆的地方在于 C 如何处理数组 _表达式_。 N1570 :
让我们看看以下声明:
arr
是int
的 10 元素数组;它指的是一个足够大的连续内存块以存储 10int
值。第二个声明中的 表达式arr
是数组类型,但由于它不是&
或sizeof
的操作数并且它不是字符串, 表达式 的类型变为“指向int
的指针”,值为第一个元素的地址,或&arr[0]
。parr
是指向int的指针;它指的是一块大到足以容纳单个int
对象的地址的内存块。如上所述,它被初始化为指向arr
中的第一个元素。这是一个假设的内存映射,显示了两者之间的关系(假设 16 位整数和 32 位地址):
类型对于
sizeof
和&
;sizeof arr == 10 * sizeof (int)
, which in this case is 20, whereassizeof parr == sizeof (int *)
, which in this case is 4. Similarly, the type of the expression&arr
isint (*)[10]
,或指向int
的类型是&parr
int **
的指针,或指向int
的指针---
。请注意,表达式
arr
和&arr
将产生相同的 _值_(arr
中第一个元素的地址),但表达式的类型不同(int *
和int (*)[10]
分别)。这在使用指针算法时会有所不同。例如,给定:“之前”行应该为所有三个表达式打印相同的值(在我们的假设地图中,
0x10008000
)。 The “after” line should show three different values:0x10008000
,0x10008002
(base plussizeof (int)
), and0x10008014
(base plussizeof (int [10])
)。现在让我们回到上面的第二段:在大多数情况下,数组 表达式 被转换为指针类型。我们来看下标表达式
arr[i]
。由于表达式arr
没有作为sizeof
或&
的操作数出现,并且由于它不是用于初始化另一个数组的字符串文字,类型从“int
”的10元素数组转换为“指向int
的指针”,并且正在对该 指针 值应用下标操作。实际上,当您查看 C 语言定义时,您会看到以下语言:实际上,这意味着您可以将下标运算符应用于指针对象 ,就好像 它是一个数组一样。这就是为什么代码喜欢
以它的方式工作。
main
is dealing with an array ofint
, whereasfoo
is dealing with a pointer toint
, yet both are able to use the下标运算符 ,就好像 它们都在处理数组类型一样。It also means array subscripting is commutative : assuming
a
is an array expression andi
is an integer expression,a[i]
andi[a]
are both有效的表达式,并且两者都将产生相同的值。