提问



正如Joel在Stack Overflow播客#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]
 ("ABCD"[2] == 2["ABCD"]) && (2["ABCD"] == 'C') && ("ABCD"[2] == 'C')
元素进一步a的值,与*(a + 5)相同,从小学数学我们知道它们是相等的(加法是可交换的。[101]

其它参考1


因为数组访问是根据指针定义的。 a[i]的定义是*(a + i),它是可交换的。

其它参考2


我认为其他答案正在忽略某些事情。


是的,p[i]根据定义等同于*(p+i),(因为加法是可交换的)等同于*(i+p),(再次,[]运算符的定义)相当于i[p]


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


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


当两个操作数属于同一类型,或者甚至是不同的数字类型被提升为一个共同类型时,交换性就很有意义:x + y == y + x


但在这种情况下,我们特别谈论指针算法,其中一个操作数是指针而另一个是整数。(整数+整数是一个不同的操作,而指针+指针是无意义的。)


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



??另外,两个操作数都应具有算术类型或一个
??操作数应该是指向完整对象类型和另一个的指针
??应具有整数类型。



它可以很容易地说:



??另外,两个操作数都应具有算术类型,或者左侧
??操作数应是指向完整对象类型和右操作数的指针
??应具有整数类型。



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


在C ++术语中,我们实际上有两组重载+运算符,可以松散地描述为:


pointer operator+(pointer p, integer i);





pointer operator+(integer i, pointer p);


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


那么为什么会这样呢?


C ++从C继承了这个定义,它从B得到它(数组索引的交换性在1972年用户参考B中明确提到),它是从BCPL(1967年的手册)中得到的,它可能从均匀得到它早期的语言(CPL?Algol?)。[103] [104]


因此,数组索引是根据加法定义的,并且即使是指针和整数,这种加法也是可交换的,可以追溯到几十年前,也就是C语言的祖先语言。


这些语言的类型远不如现代C语言。特别是,指针和整数之间的区别经常被忽略。 (在将unsigned关键字添加到语言之前,早期的C程序员有时会使用指针作为无符号整数。)因此,由于操作数不同类型而使得加法不可交换的想法可能不会发生在这些语言的设计者。如果用户想要添加两个事物,那些事物是整数,指针还是其他东西,那么就不能用语言来阻止它。


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


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


所以现在我们arr[3]3[arr]的意思完全一样,尽管后一种形式永远不会出现在IOCCC之外。[105]

其它参考3


而且当然


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


造成这种情况的主要原因是,在设计C时,70年代,计算机没有太多内存(64KB很多),因此C编译器没有做太多的语法检查。因此,X[Y]]]被盲目地翻译成*(X+Y)


这也解释了+=++语法。 A = B + C形式的所有内容都具有相同的编译形式。但是,如果B与A是同一个对象,则可以使用汇编级优化。但是编译器没有足够的亮度来识别它,所以开发人员必须(A += C。同样,如果C1,可以使用不同的汇编级优化,并且开发人员必须明确,因为编译器没有认识到它。 (最近的编译器会这样做,所以这些天的语法很大程度上是不必要的)

其它参考4


似乎没有人提到有关黛娜与sizeof的问题:


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

其它参考5


从字面上回答这个问题。 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

其它参考6


不错的问题/答案。


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


请考虑以下声明:


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


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

其它参考7


我只是发现这个丑陋的语法可能是有用的,或者至少非常有趣,当你想要处理引用同一数组中的位置的索引数组时。它可以替换嵌套的方括号,使代码更具可读性!


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)

}


当然,我很确定在实际代码中没有用例,但无论如何我发现它很有趣:)

其它参考8


对于C中的指针,我们有


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


并且


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


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

其它参考9


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

其它参考10


它在指南中的指南和阵列中有很好的解释
由泰德詹森。


Ted Jensen将其解释为:



??事实上,这是事实,即无论何人写a[i]都可以
??替换为*(a + i)没有任何问题。实际上是编译器
??将在任何一种情况下创建相同的代码。因此我们看到了指针
??算术与数组索引相同。两种语法都会产生
??同样的结果。

??
??这不是说指针和数组
??是一回事,他们不是。我们只是说要识别
??在数组的给定元素中,我们可以选择两种语法,一种
??使用数组索引和另一个使用指针算法,其中
??产生相同的结果。

??
??现在,看着这最后
??表达,部分内容.. (a + i),是使用+的简单加法
??运算符和C表达的规则是这样的表达式
??交换。那是(a + i)与(i + a)相同。因此,我们可以
??写*(i + a)就像*(a + i)一样容易。
??但*(i + a)可能来自i[a]!所有这一切都来自好奇
??如果:


char a[20];

??
??写作


a[3] = 'x';

??
??和写作一样


3[a] = 'x';


其它参考11


我知道这个问题已得到解答,但我无法抗拒分享这个解释。


我记得编译器设计原理,
假设aint数组,int的大小是2个字节,
&安培; a的基址是1000。


a[5]如何运作 - >


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


所以,


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


Base Address of your Array a + (size of(data type for array a)*5)
i.e. 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))
i.e. 1000 + (-5*2) = 990


它将在990位置返回我的对象??。

其它参考12


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

其它参考13


在c编译器中


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


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

其它参考14


在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++无效


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


指针算术内部计算为


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

其它参考15


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


编译器将a[i]解释为*(a+i),表达式5[a]求值为*(5+a)。由于加法是可交换的,事实证明两者是相等的。因此,表达式评估为true

其它参考16


在C语言中,指针和数组彼此非常接近,数组可以以指针的形式分割。数组的名称是指向其第一个元素的指针。因此,如果acData是一个字符数组,那么acData将是其第一个元素的地址。你也可以说acData类似于& acData

其它参考17

[107] [108]


根据C标准,我们可以以指针的形式表示一维数组。


请参阅以下表达式


acData [[i]]=*(acData + i); --------->指针形式的1D数组


所以如果i=5;


cData
 ("ABCD"[2] == 2["ABCD"]) && (2["ABCD"] == 'C') && ("ABCD"[2] == 'C')
=*(acData +5);


我们也可以用下面的形式表示表达式,


cData
 ("ABCD"[2] == 2["ABCD"]) && (2["ABCD"] == 'C') && ("ABCD"[2] == 'C')
=*(5 + acData);


所以现在,我们可以写


cData
 ("ABCD"[2] == 2["ABCD"]) && (2["ABCD"] == 'C') && ("ABCD"[2] == 'C')
=5 [[cData]];


请参阅以下代码,


#include 

int main(int argc, char *argv[]) {

 char cData  [] = {'w', 'o', 'r', 'l' ,'d' }; // character array

 int index = 0;

 for(index = 0; index < sizeof(cData ); ++index)
 {
     printf("Array element access by pointer = %c\n\n",cData[index]);

     printf("Array element access by   array = %c\n\n",index[cData]);
 }


    return 0;
}


参考文献,
https://aticleworld.com/array-in-c/[109]

其它参考18


指针类型


1)指向数据的指针


int *ptr;


2)const指向数据的指针


int const *ptr;


3)const指向const数据的指针


int const *const ptr;


并且数组是我们列表中的(2)类型
当您定义数组时,该指针中的地址初始化

我们知道我们不能在程序中更改或修改const值因为它在编译时会抛出 ERROR


我找到的主要差异是......



我们可以通过地址重新初始化指针,但不能用数组重新初始化指针。



======结果
并回到你的问题...

a
 ("ABCD"[2] == 2["ABCD"]) && (2["ABCD"] == 'C') && ("ABCD"[2] == 'C')
只不过是*(a + 5)

您可以轻松理解

a - 包含地址(人们称之为基地址)就像我们列表中的(2)类型的指针一样
[[]] - 该操作符可以用指针*替换。


所以最后......


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