user-0 提问: 运算符问题

回复:
user: user-1
content: 弱弱的问一下各位  你们用的什么软件编写代码  新手  专业需要学习  但是个人也比较喜爱

user: user-2
content: 不要写这种代码，容易把人搞迷糊

user: user-3
content: C语言怪异代码大赛历届作品大全 1984～1996，1998，2000，2001，2004～2006，2011，2012http://download.csdn.net/detail/zhao4zhong1/5538847

user: user-4
content: 应该是贪心法，在很多书上都是这么说的，

user: user-5
content: 1.运算符的优先级问题 2.编译器的处理方式 3.最好带括号，这样的代码简单易懂，容易理解

user: user-3
content: //C++ Operators //  Operators specify an evaluation to be performed on one of the following: //    One operand (unary operator) //    Two operands (binary operator) //    Three operands (ternary operator) //  The C++ language includes all C operators and adds several new operators. //  Table 1.1 lists the operators available in Microsoft C++. //  Operators follow a strict precedence which defines the evaluation order of //expressions containing these operators.  Operators associate with either the //expression on their left or the expression on their right;    this is called //“associativity.” Operators in the same group have equal precedence and are //evaluated left to right in an expression unless explicitly forced by a pair of //parentheses, ( ). //  Table 1.1 shows the precedence and associativity of C++ operators //  (from highest to lowest precedence). // //Table 1.1   C++ Operator Precedence and Associativity // The highest precedence level is at the top of the table. //+------------------+-----------------------------------------+---------------+ //| Operator         | Name or Meaning                         | Associativity | //+------------------+-----------------------------------------+---------------+ //| ::               | Scope resolution                        | None          | //| ::               | Global                                  | None          | //| [ ]              | Array subscript                         | Left to right | //| ( )              | Function call                           | Left to right | //| ( )              | Conversion                              | None          | //| .                | Member selection (object)               | Left to right | //| ->               | Member selection (pointer)              | Left to right | //| ++               | Postfix increment                       | None          | //| --               | Postfix decrement                       | None          | //| new              | Allocate object                         | None          | //| delete           | Deallocate object                       | None          | //| delete[ ]        | Deallocate object                       | None          | //| ++               | Prefix increment                        | None          | //| --               | Prefix decrement                        | None          | //| *                | Dereference                             | None          | //| &                | Address-of                              | None          | //| +                | Unary plus                              | None          | //| -                | Arithmetic negation (unary)             | None          | //| !                | Logical NOT                             | None          | //| ~                | Bitwise complement                      | None          | //| sizeof           | Size of object                          | None          | //| sizeof ( )       | Size of type                            | None          | //| typeid( )        | type name                               | None          | //| (type)           | Type cast (conversion)                  | Right to left | //| const_cast       | Type cast (conversion)                  | None          | //| dynamic_cast     | Type cast (conversion)                  | None          | //| reinterpret_cast | Type cast (conversion)                  | None          | //| static_cast      | Type cast (conversion)                  | None          | //| .*               | Apply pointer to class member (objects) | Left to right | //| ->*              | Dereference pointer to class member     | Left to right | //| *                | Multiplication                          | Left to right | //| /                | Division                                | Left to right | //| %                | Remainder (modulus)                     | Left to right | //| +                | Addition                                | Left to right | //| -                | Subtraction                             | Left to right | //| <<               | Left shift                              | Left to right | //| >>               | Right shift                             | Left to right | //| <                | Less than                               | Left to right | //| >                | Greater than                            | Left to right | //| <=               | Less than or equal to                   | Left to right | //| >=               | Greater than or equal to                | Left to right | //| ==               | Equality                                | Left to right | //| !=               | Inequality                              | Left to right | //| &                | Bitwise AND                             | Left to right | //| ^                | Bitwise exclusive OR                    | Left to right | //| |                | Bitwise OR                              | Left to right | //| &&               | Logical AND                             | Left to right | //| ||               | Logical OR                              | Left to right | //| e1?e2:e3         | Conditional                             | Right to left | //| =                | Assignment                              | Right to left | //| *=               | Multiplication assignment               | Right to left | //| /=               | Division assignment                     | Right to left | //| %=               | Modulus assignment                      | Right to left | //| +=               | Addition assignment                     | Right to left | //| -=               | Subtraction assignment                  | Right to left | //| <<=              | Left-shift assignment                   | Right to left | //| >>=              | Right-shift assignment                  | Right to left | //| &=               | Bitwise AND assignment                  | Right to left | //| |=               | Bitwise inclusive OR assignment         | Right to left | //| ^=               | Bitwise exclusive OR assignment         | Right to left | //| ,                | Comma                                   | Left to right | //+------------------+-----------------------------------------+---------------+

user: user-6
content: 这是运算符优先级的问题，在运算时会在操作数向左或者向右移动形成一个完整的运算定义符，应该是先++然后+，这是基本问题，楼主的基本功不扎实

user: user-7
content: 这里讨论的不是一个一般 tokenizer 应该怎么做，而是在正确解释 C 语言程序时的 token 切分的结果是如何得到了。  在这一点上，C 语言并没有留出得到多个可能结果的余地。对一个程序，它的 token 切分是唯一的。无论的 token 切分具体实现成什么样子，+++++ (假设上一个token正好在第一个+前面结束)必须被切分成 ++ ++ + ，而不是其它。

user: user-8
content: 我每次看到有人写这种代码就很来气，写这种代码的人想说明你自己对运算符的优先级掌握的很好？加个括号有这么麻烦？

user: user-9
content: 我们读写文件或字符流都是从前往后读的，但是在一些人际应答界面，不排除其采用由后向前进行cat cut，这样话，操作符分割顺序就会颠倒了。这么说源自于我参与的一个项目，其中有一个内存分割的类似token的函数，采用的就是从后向前分割的，问起理由居然是省空间！在原有的字符串空间直接改成字符串列表并实现堆栈式后进先出存放，虽然很奇葩，但不失为一种精妙的处理方法。 但是商用的或非商用的的是基于文件的分割，在这点上，就目前来说能见到的都是由前向后分割的。这也是总被处理成++ ++ +形式的原因。

user: user-7
content: 如果想验证编译器的具体实现的话，现在 g++ 和 clang 的代码都是开源的，去读就可以了。  引用 29 楼 fefe82 的回复:标准明确要求了要从前向后分割。  你可以从后向前分割（或者随意怎么分割），但是必须得到与从前向后分割相同的结果。否则你的编译器的实现就是错误的。  ===== 当然，也有时候有些编译器把故意不符合标准的处理方式当作 feature 的。不过通常情况下会用详细说明，并且有选项可以关掉。  [quote=引用 28 楼 FreeFice 的回复:] [quote=引用 27 楼 fefe82 的回复:] c++draft n4296  2.4 Preprocessing tokens  3. .................... (3.3) — Otherwise, the next preprocessing token is the longest sequence of characters that could constitute a preprocessing token, even if that would cause further lexical analysis to fail.  5 [ Example: The program fragment x+++++y is parsed as x ++ ++ + y, which, if x and y have integral types, violates a constraint on increment operators, even though the parse x ++ + ++ y might yield a correct expression. — end example ] 看来又有一个上了“标准”描述当的，这句话的有可能，源自于西方式语言严谨习惯，因为没法验证！其出台的前提在于token分割机制不确定性，因为不排除个别程序员读写代码是从尾向头读写分割的(谁叫人家有个性呢)！但是，很遗憾，商用的非商用编译器在处理程序代码时，采用的都是从头到尾式的顺序分割，也就是说虽然不排除可能，但实际上却没有。尤其是++ + ++分割牵涉到乱序分割问题，不仅仅是算法复杂度的增加，而且也是对程序稳定性的挑战。有必要为了一个不确定的技术可能而牺牲整个程序可靠便捷？更何况出现这一情况本就是建议禁止的？[/quote][/quote]

user: user-7
content: 标准明确要求了要从前向后分割。  你可以从后向前分割（或者随意怎么分割），但是必须得到与从前向后分割相同的结果。否则你的编译器的实现就是错误的。  ===== 当然，也有时候有些编译器把故意不符合标准的处理方式当作 feature 的。不过通常情况下会用详细说明，并且有选项可以关掉。  引用 28 楼 FreeFice 的回复:[quote=引用 27 楼 fefe82 的回复:] c++draft n4296  2.4 Preprocessing tokens  3. .................... (3.3) — Otherwise, the next preprocessing token is the longest sequence of characters that could constitute a preprocessing token, even if that would cause further lexical analysis to fail.  5 [ Example: The program fragment x+++++y is parsed as x ++ ++ + y, which, if x and y have integral types, violates a constraint on increment operators, even though the parse x ++ + ++ y might yield a correct expression. — end example ] 看来又有一个上了“标准”描述当的，这句话的有可能，源自于西方式语言严谨习惯，因为没法验证！其出台的前提在于token分割机制不确定性，因为不排除个别程序员读写代码是从尾向头读写分割的(谁叫人家有个性呢)！但是，很遗憾，商用的非商用编译器在处理程序代码时，采用的都是从头到尾式的顺序分割，也就是说虽然不排除可能，但实际上却没有。尤其是++ + ++分割牵涉到乱序分割问题，不仅仅是算法复杂度的增加，而且也是对程序稳定性的挑战。有必要为了一个不确定的技术可能而牺牲整个程序可靠便捷？更何况出现这一情况本就是建议禁止的？[/quote]

user: user-7
content: 分割 toke 和分割表达式是两个完全独立的过程。  分割 token 时，输入是字符，输出是 token 。  分割表达式时，输入是 token ，输出的表达式。 /// 这一句描述其实不慎准确。  至于关于分割 token 标准是如何规定，上面已经给出了。 /// 不过省略了些 ....  引用 31 楼 FreeFice 的回复:标准明确要求了要从前向后分割。 恰恰相反，标准在这一类实现上没有强制性，这牵涉到表达式是先序还是中序或后序分割问题，当是单一操作符时，是不会产生二义性问题的，但在多个操作符堆叠时，这种差异就体现出来了。好在我们通常所用的token都是先序分割的，因此就不会出现这类问题。 但要是中序或后序分割，那就有可能会成为++ + ++或+ ++ ++等其它结果(这要看操作符压栈语句怎么写)。

user: user-9
content: 标准明确要求了要从前向后分割。 恰恰相反，标准在这一类实现上没有强制性，这牵涉到表达式是先序还是中序或后序分割问题，当是单一操作符时，是不会产生二义性问题的，但在多个操作符堆叠时，这种差异就体现出来了。好在我们通常所用的token都是先序分割的，因此就不会出现这类问题。 但要是中序或后序分割，那就有可能会成为++ + ++或+ ++ ++等其它结果(这要看操作符压栈语句怎么写)。

user: user-7
content: c++draft n4296  2.4 Preprocessing tokens  3. .................... (3.3) — Otherwise, the next preprocessing token is the longest sequence of characters that could constitute a preprocessing token, even if that would cause further lexical analysis to fail.  5 [ Example: The program fragment x+++++y is parsed as x ++ ++ + y, which, if x and y have integral types, violates a constraint on increment operators, even though the parse x ++ + ++ y might yield a correct expression. — end example ]

user: user-7
content: 引用 18 楼 I_Surrender 的回复:[quote=引用 6 楼 fefe82的回复:]在将输入的字符切分为 token 的时候，每一个 token 都会尽可能用掉尽可能多的字符。  +++ 被切分的时候，会被分为 ++ +，而不是 + ++ 。第一个被切出来的 token 是 ++ 而不是 + ，因为 ++ 有两个字符，比 + （只有一个字符）多。  同样 +++++++++ 会被切分为 ++ ++ ++ ++ + 。  =========== 这跟优先级没多大关系 ... 好多人都说是优先级问题。你说的这个原因有办法验证没有？[/quote]  没有，请查标准。

user: user-9
content: 引用 27 楼 fefe82 的回复:c++draft n4296  2.4 Preprocessing tokens  3. .................... (3.3) — Otherwise, the next preprocessing token is the longest sequence of characters that could constitute a preprocessing token, even if that would cause further lexical analysis to fail.  5 [ Example: The program fragment x+++++y is parsed as x ++ ++ + y, which, if x and y have integral types, violates a constraint on increment operators, even though the parse x ++ + ++ y might yield a correct expression. — end example ] 看来又有一个上了“标准”描述当的，这句话的有可能，源自于西方式语言严谨习惯，因为没法验证！其出台的前提在于token分割机制不确定性，因为不排除个别程序员读写代码是从尾向头读写分割的(谁叫人家有个性呢)！但是，很遗憾，商用的非商用编译器在处理程序代码时，采用的都是从头到尾式的顺序分割，也就是说虽然不排除可能，但实际上却没有。尤其是++ + ++分割牵涉到乱序分割问题，不仅仅是算法复杂度的增加，而且也是对程序稳定性的挑战。有必要为了一个不确定的技术可能而牺牲整个程序可靠便捷？更何况出现这一情况本就是建议禁止的？

user: user-10
content: 引用 24 楼 庄鱼的回复:[quote=引用 18 楼 I_Surrender 的回复:] [quote=引用 6 楼 fefe82的回复:]在将输入的字符切分为 token 的时候，每一个 token 都会尽可能用掉尽可能多的字符。  +++ 被切分的时候，会被分为 ++ +，而不是 + ++ 。第一个被切出来的 token 是 ++ 而不是 + ，因为 ++ 有两个字符，比 + （只有一个字符）多。  同样 +++++++++ 会被切分为 ++ ++ ++ ++ + 。  =========== 这跟优先级没多大关系 ... 好多人都说是优先级问题。你说的这个原因有办法验证没有？[/quote] 你自己写个编译器就知道了。token分割总是就前不就后的，所以，其分割总是就目前所能解释的最大连接词(或符号)，自然就出现++ +的情况了。[/quote] 引用 18 楼 I_Surrender 的回复:[quote=引用 6 楼 fefe82的回复:]在将输入的字符切分为 token 的时候，每一个 token 都会尽可能用掉尽可能多的字符。  +++ 被切分的时候，会被分为 ++ +，而不是 + ++ 。第一个被切出来的 token 是 ++ 而不是 + ，因为 ++ 有两个字符，比 + （只有一个字符）多。  同样 +++++++++ 会被切分为 ++ ++ ++ ++ + 。  =========== 这跟优先级没多大关系 ... 好多人都说是优先级问题。你说的这个原因有办法验证没有？[/quote] 你自己写个编译器就知道了。token分割总是就前不就后的，所以，其分割总是就目前所能解释的最大连接词(或符号)，自然就出现++ +的情况了。[/quote] 嗯，明白了，我试试，非常感谢！

user: user-11
content: 学习了！不过这样写好像真的会被打哎

user: user-12
content: 很明显楼主在这里讨论的是个技术细节问题，而不是编程习惯问题，楼主纠结的不是怎么样可以得出结果，而是为什么能够出现这样的结果。这里不是运算优先级的问题，建议说优先级问题的人去看看编译原理。 非常认同#6 #7的说法，根据编译器的语义解析的贪心算法，会把+++解析成++   +，所以自然就变成了(a++)+b，只是编译器自动识别的。