【问题标题】:Why can't I do ++i++ in C-like languages?为什么我不能用类 C 语言做 ++i++?
【发布时间】:2010-12-03 10:08:46
【问题描述】:

半开玩笑半认真为什么我不能用类似 C 的语言,特别是 C# 中的 ++i++

我希望它增加值,在我的表达式中使用它,然后再次增加。

【问题讨论】:

  • 即使它确实有效,你也不应该因为你不应该讨厌生活:(。无论解释如何,在任何情况下看到 ++i++ 都会让我感到难过。
  • 我不知道有“stupid-operator-tricks”标签,谢谢约翰!
  • @zvolkov: 没有......约翰为你的问题创建了这个标签;)
  • 当我们在这里的时候,i++++ 和 i++++++ 和 i++++++++ 怎么样...
  • 看起来像模拟文字时间 eh :)

标签: c# c post-increment pre-increment


【解决方案1】:

来自C# specification

后缀递增或递减操作的操作数必须是分类为变量、属性访问或索引器访问的表达式。操作的结果是一个与操作数相同类型的 value

自增运算符只能应用于变量(和㏇),它返回一个值(不是变量)。你不能对一个值应用增量(仅仅是因为没有变量可以分配结果),所以在 C# 中你只能增加一个变量一次。

C++ 的情况实际上有所不同。 根据C++ specification

前缀递增或递减是一个lvalue表达式

这意味着在 C++ 中,您可以在前缀递增或递减的结果上调用递增。例如以下C++ code实际上是有效的:

#include <iostream>

using namespace std;

int main()
{
    int i = 13;

    (--i)++;
    cout<<i<<endl;

    (++i)--;
    cout<<i<<endl;

    return 0;
}

注意:lvalue 一词仅用于 C 和 C++。而且为了 C 中的多样性,前缀增量的结果实际上是 rvalue 所以你不能在 C 中增加增量。
C# 语言使用术语 variable_reference 表示类似的概念:

variable_reference 是分类为变量的表达式。 variable_reference 表示一个存储位置,可以访问它来获取当前值和存储新值。

【讨论】:

    【解决方案2】:

    虽然“它不是左值”的简短回答是正确,但这可能只是在乞求问题。 为什么不是左值?或者,正如我们在 C# 中所说,一个变量

    原因是因为你不能吃蛋糕也不能吃。合乎逻辑地解决:

    首先,C#中++运算符的含义,无论是后缀还是前缀,都是“取这个变量的值,递增该值,将新值赋给该变量,并产生一个值为结果”。结果产生的值要么是原始值,要么是增量值,这取决于它是后缀还是前缀。但无论哪种方式,你都会产生价值。

    其次,变量的值始终是该变量的当前内容。 (模数某些奇怪的线程场景,这些场景会让我们走得更远。)

    我希望你同意这些是完全明智的规则。

    现在应该清楚为什么 i++ 的结果不能是变量,但如果不是,让我说清楚:

    假设 i 是 10。i++ 的含义应该是“获取 i 的值 - 10 - 递增 - 11 - 存储 - i 现在是 11 - 并给出原始值作为结果 - 10”。所以当你说 print(i++) 时它应该打印 10,而 11 应该存储在 i 中。

    现在假设 i++ 的含义是返回变量,而不是。你说 print(i++) 会发生什么?你得到 i - 10 - 增加它 - 11 - 存储它 - i 现在是 11 - 并将变量作为结果返回。变量的当前值是多少? 11!这正是您不想打印的内容。

    简而言之,如果 i++ 返回一个变量,那么它将与操作符的预期含义完全完全相反!你的提议在逻辑上是不一致的,这就是为什么没有语言这样做的原因。

    【讨论】:

    • 实际上预增量应该是增加值并返回对原始对象的引用而不是值。
    • 马丁,我的解释既准确又准确;我说的是 C# 语义,而不是 C 或 C++ 语义,因为这是原始发帖人特别要求的。我会在正文中更清楚地说明这一点。
    • @Martin York:是的,这就是为什么(在那些语言中)(++i)++ 仍然有效。但是,++i++ 实际上等价于 ++(i++),因为后增量的优先级高于前增量。
    【解决方案3】:

    简答:i++ 不是“左值”,因此不能作为赋值的主题。

    【讨论】:

    • 后增量优先于前增量;你想说i++ 不是左值,不是++i。 (虽然这两种方式都不起作用)
    • ++i 是一个左值,所以(++i)++ 将是有效的,但它会写入i 两次而没有插入序列点。问题是语法指定++i++等价于`++(i++)`。
    • @Charles Bailey:++i 是 C 和 C++ 中的左值,但 IIRC 不是 C#。
    • ++i 不是 C 中的左值。尝试分配给它,看看你的编译器说什么。
    • 好点,做得好。不过,我想强调的更多是“问题专门针对 C#”,而不是“我不记得 C 和 C++ 之间的尴尬区别”。
    【解决方案4】:

    我测试了 (++i,i++) 作为解决方法:

    #include <stdio.h> 
    
    int main(){
      int i=0;
    
      printf(" i:         %i\n", i         );
      printf(" (++i,i++): %i\n", (++i,i++) );
      printf(" i:         %i\n", i         );
    }
    

    结果:

    
    i:         0
    (++i,i++): 1
    i:         2
    

    【讨论】:

    • 看起来像某种表情符号。云杉鹅倒飞?
    【解决方案5】:

    来自C# 3.0 specification 的第 7.5.9 节:

    后缀增量的操作数或 减量操作必须是 分类为变量的表达式,a 属性访问或索引器访问。 运算的结果是一个值 与操作数的类型相同。如果 后缀增量的操作数或 减量操作是一个属性或 索引器访问、属性或 indexer 必须同时具有 get 和 set 访问者。如果不是这种情况,一个 发生编译时错误。

    此外,将首先计算后增量表达式 (i++),因为它的优先级高于前增量 (++i) 运算符。

    【讨论】:

      【解决方案6】:

      因为i++ 的结果不是左值。

      【讨论】:

      • ++i 将首先被评估。
      • 实际上是后增量优先。
      【解决方案7】:

      我相信递增(或递减)运算符需要一个左值来分配。但是 ++i 不是左值,它是一个表达式。精通编译器的人可能能够澄清这种限制是否存在任何技术原因。

      【讨论】:

      • ++i的类型在C语言中的左值
      • 你能澄清一下吗?我试图编译“++i = 4;”并得到一个错误,指出操作数的左侧需要一个左值。也许我的误解在于左值的定义?我理解它是可以分配的东西。
      • 标准第 5.3.2 节第 1 段:“该值是操作数的新值;它是一个左值。” (这当然是关于“前缀++”的段落。)当然,“++i = 4;”也是尝试在没有中间序列点的情况下更改 i 的值两次,因此行为未定义。 "f(++i)" 和 "int f(int &)" 将涉及一个序列点,并且应该是合法的。
      • 好的,它绝对是 C++ 中的左值,我现在找不到 C 的明确答案。
      • .. 除非您的答案当然是关于 C,在这种情况下,++i 实际上 不是 左值。在 C99 草案 n1256 中,您会发现:“赋值表达式具有赋值后左操作数的值,但不是左值”,而它说 ++i 等价于 i+=1。此外,在 C 的大多数上下文中,任何左值都会自动转换为非左值(右值)。
      【解决方案8】:

      因为您关心下一位程序员维护(或尝试重写)您的代码,而在您因违反流行惯例而被解雇很久之后。

      【讨论】:

      • 这就是为什么它是“不道德的”,但实际上它既违法又不道德。
      • 不幸的是,许多 c++(希望在下一个标准中得到修复)是不道德的和政治上不正确的使用,但合法...
      • 这是与程序设计有关的评论,而不是与语言设计有关的评论...
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-07-31
      • 2011-05-04
      • 1970-01-01
      • 2016-02-20
      • 1970-01-01
      相关资源
      最近更新 更多