int main ()
{
int a = 5,b = 2;
printf("%d",a+++++b);
return 0;
}
此代码出现以下错误:
错误:左值作为递增操作数
但如果我在a++ +
和++b
中放置空格,那么它可以正常工作。
int main ()
{
int a = 5,b = 2;
printf("%d",a++ + ++b);
return 0;
}
第一个例子中的错误是什么意思?
答案 0 :(得分:178)
编译器分阶段编写。第一个阶段称为词法分析器,将字符转换为符号结构。所以“++”变得像enum SYMBOL_PLUSPLUS
。之后,解析器阶段将其转换为抽象语法树,但它无法更改符号。您可以通过插入空格来影响词法分析器(除非它们在引号中,否则为结束符号。)
普通词法分析器是贪婪的(有一些例外),所以你的代码被解释为
a++ ++ +b
解析器的输入是一个符号流,因此您的代码将类似于:
[ SYMBOL_NAME(name = "a"),
SYMBOL_PLUS_PLUS,
SYMBOL_PLUS_PLUS,
SYMBOL_PLUS,
SYMBOL_NAME(name = "b")
]
解析器认为语法错误。 (编辑基于注释:语义上不正确,因为你不能将++应用于一个r值,而++导致这个值)
a+++b
是
a++ +b
哪个好。你的其他例子也是如此。
答案 1 :(得分:91)
printf("%d",a+++++b);
被解释为(a++)++ + b
。
++
(后缀)不评估为lvalue
,但要求其操作数为lvalue
。
<子>! 6.4 / 4说 下一个预处理令牌是可以构成预处理令牌的最长字符序列“ 子>
答案 2 :(得分:30)
词法分析器使用通常称为“最大蒙克”算法来创建令牌。这意味着当它正在读取字符时,它会一直读取字符,直到它遇到的东西不能与它已经拥有的东西相同的一部分(例如,如果它已经读取了数字,那么它具有的是一个数字,如果遇到的话一个A
,它知道它不能成为数字的一部分。所以它停止并将A
留在输入缓冲区中以用作下一个标记的开头)。然后它将该标记返回给解析器。
在这种情况下,这意味着+++++
被列为a ++ ++ + b
。由于第一个后增量产生一个rvalue,第二个不能应用于它,并且编译器会给出错误。
只是FWIW,在C ++中你可以重载operator++
来产生一个左值,这样就可以了。例如:
struct bad_code {
bad_code &operator++(int) {
return *this;
}
int operator+(bad_code const &other) {
return 1;
}
};
int main() {
bad_code a, b;
int c = a+++++b;
return 0;
}
用我熟悉的C ++编译器编译和运行(尽管它什么都不做)(VC ++,g ++,Comeau)。
答案 3 :(得分:14)
这个确切示例包含在draft C99 standard( C11 中的相同详细信息)部分6.4 Lexical elements paragraph 4中,其中包含:
如果输入流已被解析为预处理令牌,则最多为a 给定字符,下一个预处理标记是最长的序列 可以构成预处理令牌的字符。 [...]
也称为maximal munch rule,用于词法分析以避免歧义,并通过尽可能多的元素来形成有效的令牌。
该段落还有两个例子,第二个是你问题的完全匹配,如下:
示例2程序片段x +++++ y被解析为x ++ ++ + y,其中 违反增量运算符的约束,即使解析x ++ + ++ y可能会产生正确的表达式。
告诉我们:
a+++++b
将被解析为:
a ++ ++ + b
违反了后增量的约束,因为第一个后增量的结果是一个rvalue,后增量需要一个左值。这一点在6.5.2.4
Postfix递增和递减运算符部分中有所说明(强调我的):
后缀增量或减量运算符的操作数应具有 合格或不合格的实物或指针类型,应为a 可修改的左值。
和
postfix ++运算符的结果是操作数的值。
C ++ Gotchas 这本书也涵盖了Gotcha #17
Maximal Munch Problems中的这种情况,它也是 C ++ 中的同样问题,它也提供了一些例子。它解释了在处理以下字符集时:
->*
词法分析器可以做以下三件事之一:
-
,>
和*
->
和*
->*
maximal munch 规则允许它避免这些歧义。作者指出它(在C ++上下文中):
解决了比它造成的问题更多的问题,但有两个常见的问题 情况,这是一个烦恼。
第一个例子是模板,其模板参数也是模板(在C ++ 11 中解决),例如:
list<vector<string>> lovos; // error!
^^
将结束尖括号解释为 shift运算符,因此需要一个空格来消除歧义:
list< vector<string> > lovos;
^
第二种情况涉及指针的默认参数,例如:
void process( const char *= 0 ); // error!
^^
将被解释为*=
赋值运算符,在这种情况下,解决方案是在声明中命名参数。
答案 4 :(得分:12)
您的编译器拼命尝试解析a+++++b
,并将其解释为(a++)++ +b
。现在,后增量(a++
)的结果不是lvalue,即它不能再次递增。
请不要在生产质量计划中编写此类代码。想想那个需要解释你的代码的穷人。
答案 5 :(得分:9)
(a++)++ +b
a ++返回前一个值,即右值。你无法增加它。
答案 6 :(得分:7)
因为它会导致未定义的行为。
是哪一个?
c = (a++)++ + b
c = (a) + ++(++b)
c = (a++) + (++b)
是的,你和编译器都不知道。
击>
编辑:
真正的原因是其他人所说的那个:
它被解释为(a++)++ + b
。
但是后增量需要一个左值(这是一个带名字的变量)但是(a ++)返回一个不能递增的右值,从而导致你得到的错误信息。
向其他人指出这一点。
答案 7 :(得分:5)
我认为编译器将其视为
c =((a ++)++)+ b
++
必须具有可修改的值作为操作数。 a是可以修改的值。 a++
但是'rvalue',它无法修改。
顺便说一句,我在GCC C上看到的错误是相同的,但措辞不同:lvalue required as increment operand
。
答案 8 :(得分:0)
按照先行顺序
1。++(预递增)
2. +-(加法或减法)</ p>
3。“ x” +“ y”添加两个序列
int a = 5,b = 2;
printf("%d",a++ + ++b); //a is 5 since it is post increment b is 3 pre increment
return 0; //it is 5+3=8