提问人:Barshan Das 提问时间:3/17/2011 最后编辑:LundinBarshan Das 更新时间:4/3/2020 访问量:10412
为什么 a+++++b 不起作用?
Why doesn't a+++++b work?
问:
int main ()
{
int a = 5,b = 2;
printf("%d",a+++++b);
return 0;
}
此代码给出以下错误:
错误:需要 lValue 作为增量操作数
但是,如果我在整个 和 中放置空格,那么它就可以正常工作。a++ +
++b
int main ()
{
int a = 5,b = 2;
printf("%d",a++ + ++b);
return 0;
}
第一个示例中的错误是什么意思?
答:
(a++)++ +b
a++ 返回上一个值,即 rValue。你不能增加这个。
编译器拼命尝试解析 ,并将其解释为 。现在,后递增 () 的结果不是左值,即它不能再次递增。a+++++b
(a++)++ +b
a++
请不要在生产质量程序中编写此类代码。想想那个可怜的家伙在你身后,需要解释你的代码。
printf("%d",a+++++b);
被解释为根据最大蒙克规则!。(a++)++ + b
++
(postfix) 的计算结果不为 an,但它要求其操作数为 .lvalue
lvalue
! 6.4/4 说 下一个预处理令牌是可以构成预处理令牌的最长字符序列”
因为它会导致未定义的行为。
是哪一个?
c = (a++)++ + b
c = (a) + ++(++b)
c = (a++) + (++b)
是的,你和编译器都不知道。
编辑:
真正的原因是其他人所说的:
它被解释为 .(a++)++ + b
但是 post increment 需要一个 lValue(这是一个有名称的变量),但 (A++) 返回一个不能递增的 rValue,从而导致您收到错误消息。
感谢其他人指出这一点。
评论
a+++b
a++ + b
a++ ++ +b
a+++++b
(a++)++)+b
编译器是分阶段编写的。第一阶段称为词法分析器,将字符转换为符号结构。所以“++”变成了一个类似的东西。稍后,解析器阶段将其转换为抽象语法树,但它无法更改符号。您可以通过插入空格(结束符号,除非它们在引号中)来影响词法分析器。enum SYMBOL_PLUSPLUS
普通词法分析器是贪婪的(有一些例外),所以你的代码被解释为
a++ ++ +b
解析器的输入是符号流,因此您的代码将如下所示:
[ SYMBOL_NAME(name = "a"),
SYMBOL_PLUS_PLUS,
SYMBOL_PLUS_PLUS,
SYMBOL_PLUS,
SYMBOL_NAME(name = "b")
]
解析器认为这在语法上不正确。(基于注释的编辑:语义不正确,因为您不能将 ++ 应用于 r 值,而 a++ 会导致)
a+++b
是
a++ +b
这没关系。你的其他例子也是如此。
评论
a++
a++
导致右值。
x = 10&987&&654&&321
x = 10&987&&654&&&321
我认为编译器将其视为
c = ((a++)++)+b
++
必须具有可修改的值作为操作数。a 是可以修改的值。 但是是“rValue”,则无法修改。a++
顺便说一句,我在 GCC C 上看到的错误是相同的,但措辞不同:.lvalue required as increment operand
词法分析器使用通常称为“最大咀嚼”算法来创建标记。这意味着当它读取字符时,它会继续读取字符,直到它遇到不能与它已经拥有的标记相同的标记的一部分的东西(例如,如果它一直在读取数字,那么它所拥有的是一个数字,如果它遇到一个,它知道它不能是数字的一部分。 所以它会停止并留在输入缓冲区中用作下一个标记的开头)。然后,它将该令牌返回给分析器。A
A
在本例中,这意味着被词法化为 .由于第一个后增量产生一个 rvalue,因此第二个不能应用于它,并且编译器会给出错误。+++++
a ++ ++ + b
只是 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)编译和运行(尽管它什么都不做)。
评论
16FA
0x
16
FA
0x
x
C99 标准草案(与 C11 中的细节相同)第 6.4 节词汇要素第 4 段中涵盖了这个确切的例子,其中说:
如果输入流已解析为预处理令牌,则最多 给定字符,下一个预处理标记是最长的序列 可以构成预处理令牌的字符。[...]
这也被称为最大蒙克规则,用于词汇分析以避免歧义,并通过获取尽可能多的元素来形成有效的标记。
该段落还有两个示例,第二个示例与您的问题完全匹配,如下所示:
示例 2 程序片段 x+++++y 被解析为 x ++++ + y,其中 违反对增量运算符的约束,即使解析 x ++ + ++ y 可能会产生正确的表达式。
它告诉我们:
a+++++b
将被解析为:
a ++ ++ + b
这违反了对后递增的约束,因为第一个后递增的结果是右值,而后递增量需要左值。这在后缀递增和递减运算符一节中有所介绍,其中说(强调我的):6.5.2.4
后缀递增或递减运算符的操作数应具有 合格或不合格的实数或指针类型,应为 可修改的左值。
和
postfix ++ 运算符的结果是操作数的值。
C++ Gotchas一书在Maximal Munch Problems中也介绍了这种情况,在C++中也是同样的问题,并且还给出了一些示例。它解释了在处理以下字符集时:Gotcha #17
->*
词法分析器可以执行以下三项操作之一:
- 将其视为三个标记:和
-
>
*
- 将其视为两个标记:和
->
*
- 将其视为一个令牌:
->*
最大咀嚼规则允许它避免这些歧义。作者指出,它(在 C++ 上下文中):
解决的问题比它引起的要多得多,但在两个共同点上 情况,这很烦人。
第一个示例是模板,其模板参数也是模板(在 C++11 中已解决),例如:
list<vector<string>> lovos; // error!
^^
它将右尖括号解释为移位运算符,因此需要一个空格来消除歧义:
list< vector<string> > lovos;
^
第二种情况涉及指针的默认参数,例如:
void process( const char *= 0 ); // error!
^^
将被解释为赋值运算符,在这种情况下,解决方案是在声明中命名参数。*=
评论
>>
遵循此优先顺序
1.++(预增量)
2.+ -(加法或减法)
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
C 规范的第 6.4 节第 4 段实际上正好涵盖了这种情况:
如果输入流已解析为预处理标记,直到给定字符,则下一个 预处理令牌是可以构成预处理的最长字符序列 令 牌。此规则有一个例外:仅识别标头名称预处理令牌 在 #include 预处理指令中以及在 #pragma 内实现定义的位置 指令。在此类上下文中,可以是标头名称或字符串的字符序列 literal 被识别为前者。
示例 1 程序片段 1Ex 被解析为预处理数字标记(不是有效的浮点数或整数标记 常量令牌),即使作为预处理令牌 1 和 Ex 对的解析可能会生成有效的表达式(对于 例如,如果 Ex 是定义为 +1 的宏。类似地,程序片段 1E1 被解析为预处理数字(1 这是一个有效的浮动常量标记),无论 E 是否为宏名称。
示例 2 程序片段 x+++++y 被解析为 x ++ ++ + y ,这违反了对增量运算符的约束, 即使解析 x ++ + ++ y 可能会产生正确的表达式
评论
+++y
被解析为 x++++
+ y,这违反了对增量运算符的约束,即使解析x ++ + ++ y
可能会产生正确的表达式。