Lov*_*ure 0 c lvalue language-lawyer pre-increment post-increment
为什么按照标准不允许a++ = b;,而c[i++] = d;允许呢?
(显然,a++ = b;这是一种不好的风格,但这是一个关于仔细阅读 C 语言标准的问题。)
这是强制性的最小示例:
#include <stdio.h>
int main(void)
{
int a = 10;
int b = 20;
int i = 1;
int c[] = {30, 40};
int d = 50;
a++ = b; // error
c[i++] = d;
printf("%d\n", a); // [21]
printf("%d\n", b); // 20
printf("%d\n", i); // 2
printf("%d\n", c[0]); // 30
printf("%d\n", c[1]); // 50
printf("%d\n", d); // 50
return 0;
}
Run Code Online (Sandbox Code Playgroud)
-std=c90当使用或进行编译时,GCC 会发出以下错误-std-c17:
error: lvalue required as left operand of assignment
Run Code Online (Sandbox Code Playgroud)
根据 K&R (2e)、A7.3.4 和 A7.4.1
++[后缀/前缀或]的结果--不是左值。
a++被认为不是左值,但是从什么措辞可以明确看出它c[i++] 是左值?至于C11标准,我找不到任何关于这两者的规定。
对于它的价值:如果不被禁止,我会解释a++ = b;为a = b; a = a+1;.
编辑:
有些人(有理由)问为什么人们会在语义上假设一个陈述是a++ = b;有道理的。
我经常尝试将棘手的语法结构转换为等效但更简单的东西。(我们都承认,前置和后置自增/自减运算符不仅很棘手;它们是语法灾难:它们可以深深嵌入到语句中,但具有必须在之前或之后执行的效果。)我通常会假设任何形式的非病态陈述
陈述(
++w,--x,y++,z--)
相当于
w += 1;
x -= 1;
陈述(w,x,y,z)
y += 1;
z -= 1;
其中语句前和语句后分配以实现定义的方式排序。当然,问题是什么算作“非病态”(或者我们是否应该将其定义为“前增量和后增量之间的顺序无关紧要的情况”),但是,把这个暂时先把问题放在一边,程序员假设前置和后置自增/自减表达式在语法上等同于删除了这些运算符的相应形式,这并非没有道理。
说“此类运算符剥夺其左值质量的参数表达式”是完全有效的,并且确实回答了我的问题,但是:
++或--] 的结果不是左值。”)似乎只是为了禁止像a++ = b;.ike*_*ami 12
从什么措辞中可以明确看出这
c[i++]是左值?
c[i]定义为*(c+i)
6.5.2.1.2 后缀表达式后跟方括号中的表达式
[]是数组对象元素的下标指定。下标运算符的定义[]与E1[E2]相同(*((E1)+(E2)))。[...]
这给了我们一个左值。
6.5.3.2.4 一元
*运算符表示间接。如果操作数指向函数,则结果是函数指示符;如果它指向一个对象,则结果是指定该对象的左值。[...]
你能修改 的结果吗5+1?当然不是。(5+1) = 3;完全没有意义。结果5+1不是可以保持价值的东西。您稍后无法获取分配给它的值。分配给 的结果5+1完全是胡说八道。
的结果a++是 的旧值a。它不可能是a它本身,因为它a不再具有正确的值。它只是一个短暂的值,就像 的结果一样5+1。它不是一个可以保值的东西。分配给它绝对没有意义。
用技术术语来说,a++它不是一个可修改的左值,因为它不是一个左值,因为它不潜在地引用一个对象。
6.3.2.1.1 左值是一个可能指定对象的表达式(具有除 void 之外的对象类型)。[...]
参考资料来自 C17 标准。
c[i++] = d;
这很好,因为它可以翻译为:
c[i] = d;
i++;
Run Code Online (Sandbox Code Playgroud)
a++=b;
它不能扩展到任何有意义或有用的东西。这两个操作应该像这样展开:
a++;
<result_of_a++> = b;
Run Code Online (Sandbox Code Playgroud)
这是无法实现的,因为 a++ 的结果是一个值,而不是一个“位置”。您不能将一个值分配给另一个值。