我会尽量给你一个技术上正确的解释,这样你就会知道发生了什么。不是很复杂,但确实违反直觉。
简介:
在 C 中,“左值”基本上表示“可分配”对象,这些对象在内存中的某个位置,而“右值”则表示“概念”值(不需要特别放在任何地方)。
例如,如果您定义int a = 5;,则a 是一个int 类型的左值,值为5。它也可以被解释为(或者更确切地说,转换为)一个int 类型的右值。这样的右值仍会被认为等于 5,但它不再包含有关 a 在内存中的位置的信息。
有些表达式需要左值(比如 operator= 的左侧,因为你必须分配给一个对象),有些需要右值(比如 operator+,因为你在相加时只需要整数值,或者右侧运算符=)。如果一个表达式需要一个右值但你传递了一个左值,那么它将被转换为一个右值。
此外,只有右值被传递给 C 中的函数(这意味着 C 是严格按值调用,而不是按引用调用)。
一些例子:
int a = 1;
a; // a is an lvalue of type int and value 1
a = a+3; // here the `a` is converted to an rvalue of type int and value 1, then after the addition there's an assignment, on the lhs there's an lvalue `a` and an rvalue `4`
从左值到右值的转换通常是微不足道的并且不引人注意(这就像从标有a 的架子上拿数字5)。数组基本上是这里的例外。
重要的是:C 中没有数组类型的右值。有指针左值和右值,整数左值和右值,结构左值和右值等......但只有左值数组。当您尝试将数组类型的左值转换为右值时,您不再拥有数组,而是拥有指向数组第一个成员的指针。这就是 C(和 C++)中的数组混淆的根源。
说明:
array
*(array)
array+1
*(array)+1
array 是int[3][5] 类型的左值(3 个整数的数组,5 个整数)。当您尝试将其传递给函数时,它会收到一个 int (*)[5] 类型的指针(指向 5 个整数数组的指针),因为这是左值到右值转换后剩下的内容。
*(array) 更狡猾。首先执行左值到右值,得到int(*)[5] 类型的右值,然后operator* 采用该右值并返回int[5] 类型的左值,然后您尝试将其传递给函数。因此它再次被转换为一个右值,产生一个int*。
array+1 使数组转换为 int(*)[5] 类型的右值,并且该右值加一,因此(根据指针算术规则)指针向前移动 1 * sizeof(int[5]) 字节。
*(array)+1:参见前面的 2 点,但 int* 类型的最终右值会增加,再次按照指针算术规则,增加 1 * sizeof(int)。
这里不神秘!