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

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


当前回答

现在有点历史了。在其他语言中,BCPL对C的早期发展产生了相当大的影响。如果您在BCPL中声明的数组类似于:

let V = vec 10

实际上分配了11个字的内存,而不是10个。通常V是第一个,并包含紧接其后的单词的地址。因此,与C不同,命名V到那个位置,并获取数组第0个元素的地址。因此,BCPL中的数组间接寻址表示为

let J = V!5

真的不得不做J=!(V+5)(使用BCPL语法),因为需要获取V以获得阵列的基地址。因此,V!5和5!V是同义词。据坊间观察,WAFL(Warwick Functional Language,沃里克函数语言)是用BCPL编写的,据我所知,在访问用作数据存储的节点时,倾向于使用后一种语法而不是前一种语法。当然这是35到40年前的某个地方,所以我的记忆有点生疏

省去了额外的存储字,并在命名数组时让编译器插入数组的基地址,这一创新是后来才出现的。根据C历史论文,这大约发生在C中添加结构的时候。

注意!BCPL中既有一元前缀运算符,也有二元中缀运算符,在这两种情况下都是间接操作。只是二进制形式在执行间接操作之前包括两个操作数的相加。鉴于BCPL(和B)面向单词的性质,这实际上很有意义。当C获得数据类型时,“指针和整数”的限制就变得必要了,sizeof也成了一件事。

其他回答

关于Dinah的sizeof问题,似乎没有人提到过一件事:

只能向指针添加整数,不能将两个指针相加。这样,当将指针添加到整数或将整数添加到指针时,编译器总是知道需要考虑哪个位的大小。

In C

 int a[]={10,20,30,40,50};
 int *p=a;
 printf("%d\n",*p++);//output will be 10
 printf("%d\n",*a++);//will give an error

指针p是“变量”,数组名称a是“助记符”或“同义词”,因此p++有效,而a++无效。

a[2]等于2[a],因为这两者的内部运算都是“指针算术”,内部计算为*(a+2)等于*(2+a)

很好的问题/答案。

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

考虑以下声明:

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

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

不是答案,只是一些思考的食物。如果类具有重载的索引/下标运算符,则表达式0[x]将不起作用:

class Sub
{
public:
    int operator [](size_t nIndex)
    {
        return 0;
    }   
};

int main()
{
    Sub s;
    s[0];
    0[s]; // ERROR 
}

由于我们无法访问int类,因此无法执行此操作:

class int
{
   int operator[](const Sub&);
};

当然,还有

 ("ABCD"[2] == 2["ABCD"]) && (2["ABCD"] == 'C') && ("ABCD"[2] == 'C')

其主要原因是,在70年代设计C时,计算机没有太多内存(64KB是很多),因此C编译器没有做太多语法检查。因此,“X[Y]”相当盲目地翻译为“*(X+Y)”

这也解释了“+=”和“++”语法。“A=B+C”形式的所有对象都具有相同的编译形式。但是,如果B是与A相同的对象,则可以进行汇编级优化。但是编译器不够明亮,无法识别它,所以开发人员不得不(A+=C)。类似地,如果C为1,则可以使用不同的汇编级优化,开发人员再次必须使其显式,因为编译器无法识别它