正如Joel在Stack Overflow podcast#34中指出的,在C编程语言(又名:K&R)中,在C中提到了数组的这个属性:a[5]==5[a]
乔尔说这是因为指针算术,但我还是不明白。 为什么a[5]==5[a]
?
C标准定义[]
运算符如下:
a[b]==*(a+b)
因此A[5]
的计算结果为:
*(a + 5)
和5[a]
的计算结果为:
*(5 + a)
a
是指向数组第一个元素的指针。 a[5]
是距离a
还有5个元素的值,与*(a+5)
相同,从小学数学中我们知道它们是相等的(加法是交换的)。
因为数组访问是根据指针定义的。 a[i]
定义为*(a+i)
,它是可交换的。
我认为其他答案漏掉了一些东西。
是的,p[i]
根据定义等效于*(p+i)
,后者(因为加法是可交换的)等效于*(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);
和
pointer operator+(integer i, pointer p);
其中只有第一项才是真正必要的。
那么为什么会这样呢?
C++继承了C的这个定义,C从B得到这个定义(1972年用户对B的引用中明确提到了数组索引的可交换性),C从BCPL得到这个定义(1967年的手册),而BCPL很可能从更早的语言(CPL?ALGOL?)得到这个定义。
因此,数组索引是用加法定义的,而且加法,即使是指针和整数的加法,也是可交换的,这可以追溯到几十年前C语言的祖先语言。
这些语言的类型比现代C语言的强得多。 特别是,指针和整数之间的区别经常被忽略。 (在unsigned
关键字被添加到语言之前,早期的C程序员有时将指针用作无符号整数。) 因此,由于操作数的类型不同,所以将加法变为非交换加法的想法可能不会出现在这些语言的设计者身上。 如果用户想要添加两个“东西”,不管这些“东西”是整数,指针还是其他东西,都不能由语言来阻止。
多年来,对该规则的任何修改都会破坏现有的代码(尽管1989年ANSI C标准可能是一个很好的机会)。
更改C和/或C++,要求将指针放在左边,将整数放在右边,可能会破坏一些现有代码,但不会损失真正的表达能力。
因此,现在我们有了arr[3]
和3[arr]
,它们的含义完全相同,尽管后一种形式永远不应该出现在IOCCC之外。