为什么+++++ b不起作用?

时间:2011-03-17 15:25:23

标签: c lvalue

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;
}

第一个例子中的错误是什么意思?

9 个答案:

答案 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