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

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


当前回答

不是答案,只是一些思考的食物。如果类具有重载的索引/下标运算符,则表达式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&);
};

其他回答

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

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

因此,[5]将评估:

*(a + 5)

并且5[a]将评估:

*(5 + a)

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

不是答案,只是一些思考的食物。如果类具有重载的索引/下标运算符,则表达式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&);
};

现在有点历史了。在其他语言中,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也成了一件事。

嗯,这是一个只有语言支持才能实现的功能。

编译器将a[i]解释为*(a+i),表达式5[a]的计算结果为*(5+a)。由于加法是可交换的,结果证明两者相等。因此,表达式的计算结果为true。

它在C语言中的指针和阵列教程中有很好的解释由Ted Jensen撰写。

Ted Jensen解释为:

事实上,这是真的,也就是说,无论你在哪里写一个替换为*(a+i)而没有任何问题。事实上,编译器将在任何情况下创建相同的代码。因此,我们看到了指针算术与数组索引相同。任一语法都会产生同样的结果。这并不是说指针和数组是一样的东西,它们不是。我们这么说只是为了确定对于数组的给定元素,我们可以选择两种语法,一种使用数组索引,另一种使用指针算法产生相同的结果。现在,看看最后一个表达式的一部分..(a+i)是使用+运算符和C的规则声明这样的表达式是交换的。即(a+i)与(i+a)相同。因此,我们可以写*(i+a)和*(a+i)一样容易。但*(i+a)可能来自i[a]!所有这些都带来了好奇如果:字符a[20];写a[3]=“x”;与写作相同3[a]=“x”;