对于数组,为什么会出现 == 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
)。 同样,如果C
为1
,则可以获得不同的汇编级别优化,而且开发人员也必须明确说明,因为编译器不能识别它。 (最近编译器是这样做的,因此这些语法在很大程度上是不必要的)
我想其他答案错过了一些东西。
是的, 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 + p
和i[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)