C 对于数组,为什么a[5]==5[a]?

C 对于数组,为什么a[5]==5[a]?,c,arrays,pointers,pointer-arithmetic,C,Arrays,Pointers,Pointer Arithmetic,正如Joel在(aka: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 + 5) *(5 + a) a是指向数组第一个元素的指针a[5]是距离a更远的5个元素的值,它与*(a+5)相同,从小学数学中我们知道它们是相等的(加法)。因为数组访问是根据指针定义的a

正如Joel在(aka: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 + 5)
*(5 + a)

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

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

当然还有

 ("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
,则可以使用不同的程序集级优化,开发人员必须再次将其显式化,因为编译器无法识别它。(最近的编译器确实如此,所以这些语法现在基本上是不必要的)

很好的问题/答案

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

考虑下列声明:

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

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

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


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

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

从字面上回答这个问题。
x==x

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

对于C中的指针,我们有

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

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

因此,
a[5]==5[a]是真的。

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

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)
    
}
inta[]={2,3,3,2,4};
int s=sizeof a/sizeof*a;//s==5
对于(int i=0;i我认为其他答案遗漏了一些东西

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

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

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

当两个操作数的类型相同,或者甚至是升级为公共类型的不同数值类型时,可交换性是非常有意义的:
x+y==y+x

但在本例中,我们专门讨论指针算术,其中一个操作数是指针,另一个是整数。(整数+整数是不同的运算,指针+指针是无意义的。)

C标准对
+
运算符(6.5.6)的描述如下:

对于加法,两个操作数都应具有算术类型,或者一个操作数具有算术类型 操作数应为指向完整对象类型的指针,另一个为 应具有整数类型

它可以很容易地说:

对于加法,两个操作数都应具有算术类型,或为左 操作数应是指向完整对象类型和正确操作数的指针 应具有整数类型

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

<>在C++术语中,我们确实有两组重载的<代码> +<代码>运算符,它们可以被松散地描述为:

pointer operator+(pointer p, integer i);

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

那么为什么是这样呢

C++从C继承了这个定义,C是从B继承的(数组索引的交换性在1972年被明确提到),C是从(1967年的手册)继承的,C很可能是从更早的语言(CPL?Algol?)继承的

因此,数组索引是用加法定义的,即使是指针和整数的加法,也是可交换的,这种思想可以追溯到几十年前的C语言

与现代C语言相比,这些语言的强类型性要小得多。特别是,指针和整数之间的区别常常被忽略。(早期C程序员有时将指针作为无符号整数使用,而不是将
无符号
关键字添加到语言中。)因此,将加法设为非交换的想法是因为
char a[20];
a[3] = 'x';
3[a] = 'x';
a[i]
i[a]
*(a+i)
Base Address of your Array a + (5*size of(data type for array a))
i.e. 1000 + (5*2) = 1010
Base Address of your Array a + (size of(data type for array a)*5)
i.e. 1000 + (2*5) = 1010 
Base Address of your Array a + (-5 * size of(data type for array a))
i.e. 1000 + (-5*2) = 990
 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
let V = vec 10
let J = V!5