正如Joel在Stack Overflow播客#34中指出的,在C编程语言(又名:K&R)中,提到了C:a[5]==5[a]中数组的这个属性

乔尔说这是因为指针运算,但我仍然不明白。为什么a[5]==5[a]?


当前回答

C基于BCPL。BCPL将存储器直接暴露为一系列可寻址字。一元运算符!X(也称为LV)为您提供了地址位置X的内容。为了方便起见,还有一个二进制运算符X!Y等于!(X+Y),它为您提供了位置X处数组的第Y个字的内容,或者等效地,位置Y处数组的X个字。

在C,X!Y变成了X[Y],但是!的原始BCPL语义!(X+Y)表示通过,这解释了为什么算子是可交换的。

其他回答

C标准对[]运算符的定义如下:

a[b]==*(a+b)

因此,[5]将评估:

*(a + 5)

并且5[a]将评估:

*(5 + a)

a是指向数组的第一个元素的指针。a[5]是距离a更远的5个元素的值,与*(a+5)相同,从小学数学中我们知道它们是相等的(加法是可交换的)。

我认为其他答案遗漏了一些东西。

是的,p[i]根据定义等价于*(p+i),*(i+p)(因为加法是可交换的)等效于*(i+p),而*(i+p)(同样,根据[]算子的定义)等效于i[p]。

(在array[i]中,数组名称隐式转换为指向数组第一个元素的指针。)

但在这种情况下,加法的交换性并不那么明显。

当两个操作数都是同一类型,甚至是不同的数字类型,并被提升为一个公共类型时,交换性就非常有意义:x+y==y+x。

但在本例中,我们具体讨论的是指针算术,其中一个操作数是指针,另一个是整数。(整数+整数是不同的操作,指针+指针是无意义的。)

C标准对+运算符的描述(N1570 6.5.6)表示:

对于加法,两个操作数都应为算术类型,或一个操作数应是指向完整对象类型的指针应为整数类型。

它可以很容易地说:

对于加法,两个操作数都应为算术类型,或左侧操作数应是指向完整对象类型和正确操作数的指针应为整数类型。

在这种情况下,i+p和i[p]都是非法的。

在C++术语中,我们确实有两组重载+运算符,可以粗略地描述为:

pointer operator+(pointer p, integer i);

and

pointer operator+(integer i, pointer p);

其中只有第一个是真正必要的。

那么为什么会这样呢?

C++继承了C的这一定义,后者从B获得了它(数组索引的交换性在1972年的《用户参考B》中明确提到),后者从BCPL(1967年的手册)获得了它,BCPL很可能是从更早的语言(CPL?Algol?)获得的。

因此,数组索引是用加法来定义的,即使是指针和整数的加法,也是可交换的,这一想法可以追溯到几十年前,直到C的祖先语言。

这些语言比现代C语言的强类型少得多。特别是,指针和整数之间的区别经常被忽略。(在无符号关键字被添加到语言中之前,早期的C程序员有时将指针用作无符号整数。)因此,由于操作数的类型不同,所以使加法不可交换的想法可能不会出现在这些语言的设计者身上。如果用户想添加两个“东西”,无论这些“东西”是整数、指针还是其他东西,都不能由语言来阻止。

多年来,对该规则的任何修改都会破坏现有的代码(尽管1989年的ANSI C标准可能是一个很好的机会)。

更改C和/或C++以要求将指针放在左边,将整数放在右边,这可能会破坏一些现有代码,但不会损失真正的表达能力。

所以现在我们有了arr[3]和3[arr],意思完全相同,尽管后一种形式不应该出现在IOCCC之外。

因为C编译器总是将数组表示法转换为指针表示法。a[5]=*(a+5)也是5[a]=*所以,两者都是相等的。

很好的问题/答案。

我只想指出,C指针和数组并不相同,尽管在这种情况下,差异并不是本质的。

考虑以下声明:

int a[10];
int* p = a;

在a.out中,符号a位于数组开始的地址,符号p位于存储指针的地址,指针在该内存位置的值是数组的开始。

在c编译器中

a[i]
i[a]
*(a+i)

引用数组中元素的方式不同!(一点都不奇怪)