C 对于数组,为什么a[5]==5[a]?
正如Joel在(aka:K&R)中指出的,在C中提到了数组的这个属性: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
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