对于数组,为什么会出现 == 5 ?

正如Joel在C语言编程语言 (又名:K&R)中的Stack Overflow podcast#34中指出的那样,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) ,从小学math我们知道那些是相等的(加法是交换的 )。

因为数组访问是用指针来定义的。 a[i]被定义为意味着*(a + i) ,它是可交换的。

而且当然

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

这样做的主要原因是早在70年代Cdevise时,计算机没有太多的内存(64KB),所以C编译器没有做太多的语法检查。 因此,“ X[Y] ”被相当盲目地翻译成“ *(X+Y)

这也解释了“ += ”和“ ++ ”语法。 所有forms为“ A = B + C ”的编译forms都是相同的。 但是,如果B是与A相同的对象,那么可以实现组件级优化。 但编译器还不够明白,所以开发人员必须( A += C )。 同样,如果C1 ,则可以获得不同的汇编级别优化,而且开发人员也必须明确说明,因为编译器不能识别它。 (最近编译器是这样做的,因此这些语法在很大程度上是不必要的)

我想其他答案错过了一些东西。

是的, p[i]在定义上相当于*(p+i) ,它(因为加法是可交换的)相当于*(i+p) ,它(同样由[]运算符定义)到i[p]

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

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

当两个操作数是相同的types,或者甚至不同的数字types被提升为一个通用types时,交换性是非常有意义的: x + y == y + x

但在这种情况下,我们正在专门讨论指针算术,其中一个操作数是一个指针,另一个是整数。 (整数+整数是不同的操作,而指针+指针是无稽之谈。)

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

另外,两个操作数都应该有算术types,或者一个操作数应该是一个指向完整对象types的指针,另一个操作数应该是整数types。

可以简单地说:

另外,两个操作数都应该有算术types,或者左边的操作数应该是一个指向完整对象types的指针, 右边的操作数应该是整数types。

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

用C ++来说,我们确实有两套重载+运算符,可以粗略地描述为:

 pointer operator+(pointer p, integer i); 

 pointer operator+(integer i, pointer p); 

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

那么为什么这样呢?

C ++inheritance了C的定义,C从B获得(数组索引的交换性在1972年的用户参考文献中明确提到),它从BCPL (1967年的手册)中得到,它很可能已经从较早的语言(CPL?Algol?)。

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

那些语言比现代C语言的types要less得多。 特别是指针和整数之间的区别经常被忽略。 (早期的C程序员有时使用指针作为无符号整数,然后将unsigned关键字添加到语言中。)因此,由于操作数是不同types而使得加法不可交换的想法可能不会出现在这些语言的devise者。 如果用户想要添加两个“东西”,不pipe这些“东西”是整数,指针还是其他东西,那么防止它就不是语言。

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

改变C和/或C ++要求把指针放在左边,右边的整数可能会破坏一些现有的代码,但是不会有真正的performance力损失。

所以现在我们有arr[3]3[arr]意思完全一样的东西,尽pipe后一种forms不应该出现在IOCCC之外。

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

您只能将一个整数添加到指针,不能将两个指针添加在一起。 这样,当向整数或整数指针添加一个指针时,编译器总是知道哪一个比特具有需要考虑的大小。

从字面上回答这个问题。 x == x并不总是如此

 double zero = 0.0; double a[] = { 0,0,0,0,0, zero/zero}; // NaN cout << (a[5] == 5[a] ? "true" : "false") << endl; 

版画

 false 

很好的问题/答案。

只是想指出,C指针和数组是不一样的 ,虽然在这种情况下,差异不是必需的。

考虑下面的声明:

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

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

我只是发现这个丑陋的语法可能是“有用的”,或者至less非常有趣,当你想处理一个指向位置的索引数组到相同的数组。 它可以replace嵌套的方括号,使代码更具可读性!

 int a[] = { 2 , 3 , 3 , 2 , 4 }; int s = sizeof a / sizeof *a; // s == 5 for(int i = 0 ; i < s ; ++i) { cout << a[a[a[i]]] << endl; // ... is equivalent to ... cout << i[a][a][a] << endl; // but I prefer this one, it's easier to increase the level of indirection (without loop) } 

当然,我确信在真实的代码中没有用例,但是我发现它很有趣:)

对于C中的指针,我们有

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

并且

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

因此,确实a[5] == 5[a].

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

Ted Jensen 在C中指针和arrays中有非常好的解释。

泰德詹森解释说:

事实上,这是真实的,也就是说,无论哪个人写a[i] ,都可以用*(a + i)代替,而没有任何问题。 事实上,无论哪种情况,编译器都会创build相同的代码。 因此我们看到指针算术和数组索引是一样的。 两种语法都会产生相同的结果。

这并不是说指针和数组是相同的东西,他们不是。 我们只是说,为了确定一个给定的数组元素,我们可以select两种语法,一种使用数组索引,另一种使用指针算术,从而得到相同的结果。

现在看这个最后的expression式,其中的一部分.. (a + i)是使用+运算符和C状态的规则的一个简单的加法,这种expression式是可交换的。 那就是(a + i)和(i + a)是一样的。 因此,我们可以像*(a + i)一样容易地写*(i + a) *(a + i) 。 但是*(i + a)可能来自i[a] ! 从所有这一切来的好奇的事实,如果:

 char a[20]; 

写作

 a[3] = 'x'; 

和写作一样

 3[a] = 'x'; 

我知道这个问题已经回答了,但我无法拒绝分享这个解释。

我记得编译器devise的原理,我们假设a int数组, int大小是2个字节, a基地址是1000。

a[5]如何工作 – >

 Base Address of your Array a + (5*size of(data type for array a)) ie 1000 + (5*2) = 1010 

所以,

类似地,当c代码被分解为3地址代码时, 5[a]将变成 – >

 Base Address of your Array a + (size of(data type for array a)*5) ie 1000 + (2*5) = 1010 

所以基本上这两个陈述都指向了记忆中的同一个位置,因此, a[5] = 5[a]

这个解释也是为什么数组中的负指数在C中工作的原因

即如果我访问a[-5]它会给我

 Base Address of your Array a + (-5 * size of(data type for array a)) ie 1000 + (-5*2) = 990 

它将返回我在990的对象。

在C数组中 , arr[3]3[arr]是相同的,它们的等价指针符号是*(arr + 3)*(3 + arr) 。 但相反, [arr]3[3]arr不正确,将导致语法错误,因为(arr + 3)*(3 + arr)*不是有效的expression式。 原因是解引用运算符应该放在expression式产生的地址之前,而不是地址之后。

在C编译器

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

是不同的方式来引用数组中的元素! (不是所有WEIRD)

在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 

指针是一个“variables”

数组名称是“助记符”或“同义词”

的p ++; 是有效的,但是一个++是无效的

a [2]等于2 [a],因为两者的内部操作都是

“指针算术”在内部计算为

*(a + 3)等于*(3 + a)