i ++和++ i有什么区别?

Dla*_*aor 194 c#

我看过他们在无数块的C#代码都被使用了,我想知道何时使用i++++i(i为某数量的变量一样int,float,double,等).谁知道这个?

Eri*_*ert 417

不幸的是,这个问题的典型答案是,一个人在"剩余的操作之前"进行增量,另一个人在"剩余操作之后"进行增量.虽然这直觉上得到了这个想法,但这种说法完全错了.该时间事件的顺序是在C#中非常明确的,这是强调认为++的前缀和后缀的版本都可以以不同的顺序的东西相对于其他操作的情况.

毫不奇怪,你会看到这个问题有很多错误的答案.很多"自学C#"的书也错了.此外,C#的方式与C的方式不同.许多人认为C#和C是同一种语言; 他们不是.在我看来,C#中增量和减量运算符的设计避免了C语言中这些运算符的设计缺陷.

必须回答两个问题才能确定前缀和后缀++在C#中的确切操作.第一个问题是结果什么?第二个问题是增量的副作用何时发生?

这两个问题的答案都不明显,但一旦你看到它,它实际上很简单.让我准确地为您拼出x ++和++ x为变量x做的事情.

对于前缀形式:

  1. 评估x以生成变量
  2. 变量的值将复制到临时位置
  3. 临时值递增以产生新值(不覆盖临时值!)
  4. 新值存储在变量中
  5. 操作的结果是新值(即临时的递增值)

对于后缀形式:

  1. 评估x以生成变量
  2. 变量的值将复制到临时位置
  3. 临时值递增以产生新值(不覆盖临时值!)
  4. 新值存储在变量中
  5. 操作的结果是临时值

有些事情需要注意:

首先,在两种情况下,事件的时间顺序完全相同.再次,这是绝对没有的情况下,该时间事件的顺序前缀和后缀之间变化.如果评估在其他评估之前或在其他评估之后发生,则完全是错误的.在两种情况下,评估都以完全相同的顺序发生,因为步骤1到4可以看出相同.的唯一区别就是最后一步 -结果是否是临时或新的,增加值的价值.

您可以使用简单的C#控制台应用轻松演示:

public class Application
{
    public static int currentValue = 0;

    public static void Main()
    {
        Console.WriteLine("Test 1: ++x");
        (++currentValue).TestMethod();

        Console.WriteLine("\nTest 2: x++");
        (currentValue++).TestMethod();

        Console.WriteLine("\nTest 3: ++x");
        (++currentValue).TestMethod();

        Console.ReadKey();
    }
}

public static class ExtensionMethods 
{
    public static void TestMethod(this int passedInValue) 
    {
        Console.WriteLine("Current:{0} Passed-in:{1}",
            Application.currentValue,
            passedInValue);
    }
}
Run Code Online (Sandbox Code Playgroud)

结果如下......

Test 1: ++x
Current:1 Passed-in:1

Test 2: x++
Current:2 Passed-in:1

Test 3: ++x
Current:3 Passed-in:3
Run Code Online (Sandbox Code Playgroud)

在第一个测试中,您可以看到currentValue传递给TestMethod()扩展的两者和内容都显示了与预期相同的值.

但是,在第二种情况下,人们会试着告诉你在调用之后currentValue发生的增量,但正如你从结果中看到的那样,它发生调用之前,如'Current:2'结果所示.TestMethod()

在这种情况下,首先将值currentValue存储在临时值中.接下来,将该值的递增版本存储回currentValue但不接触仍存储原始值的临时值.最后,临时传递给TestMethod().如果增量发生调用TestMethod()那就写出来的一样,非递增值的两倍,但事实并非如此.

重要的是要注意,两个currentValue++++currentValue操作返回的值都是基于临时而不是操作退出时存储在变量中的实际值.

回想一下上面的操作顺序,前两个步骤将变量的当前值复制到临时值中.那就是用来计算回报价值的东西; 在前缀版本的情况下,临时值增加,而在后缀版本的情况下,它是直接/非增量的值.初始存储到临时变量后,变量本身不会再次读取.

更简单地说,后缀版本返回从变量读取的值(即临时值),而前缀版本返回写回变量的值(即临时值的递增值).既不返回变量的值.

这一点很重要,因为变量本身可能是易失性的,并且在另一个线程上发生了变化,这意味着这些操作的返回值可能与存储在变量中的当前值不同.

令人惊讶的是,人们对优先级,关联性以及执行副作用的顺序非常困惑,我怀疑主要是因为它在C语言中如此令人困惑.C#经过精心设计,在所有这些方面都不那么混乱.对于这些问题的一些额外分析,包括我进一步证明前缀和后缀操作"及时移动东西"的想法的虚假性,请参阅:

http://blogs.msdn.com/b/ericlippert/archive/2009/08/10/precedence-vs-order-redux.aspx

这导致了这个问题:

int [] arr = {0}; int value = arr [arr [0] ++]; 值= 1?

您可能也对我之前关于此主题的文章感兴趣:

http://blogs.msdn.com/b/ericlippert/archive/2008/05/23/precedence-vs-associativity-vs-order.aspx

http://blogs.msdn.com/b/ericlippert/archive/2007/08/14/c-and-the-pit-of-despair.aspx

还有一个有趣的案例,C使得很难推断正确性:

http://blogs.msdn.com/b/ericlippert/archive/2005/04/28/bad-recursion-revisited.aspx

此外,在考虑其他具有副作用的操作时,我们会遇到类似的微妙问题,例如链式简单赋值:

http://blogs.msdn.com/b/ericlippert/archive/2010/02/11/chaining-simple-assignments-is-not-so-simple.aspx

这里有一篇有趣的帖子,说明为什么增量运算符会导致C#中的而不是变量中的:

为什么我不能用C-like语言做++ i ++?

  • @Tomas:首先,我关注*所有*C#应用程序,而不仅仅是普通应用程序的质量.非平均应用程序的数量很大.第二,它没有区别*除了它有所作为*的情况.基于实际代码中的真实错误,我每年可能会有六次问题.第三,我同意你的观点; ++的整体思想是一个非常低级的想法,在现代代码中看起来非常古怪.它实际上只是存在,因为对于类似C语言而言,拥有这样的运算符是惯用的. (23认同)
  • @Justin:我添加了一些链接.但基本上:在C中,你无法保证任何事情及时发生的事情.当同一序列点中存在两个突变时,一个符合标准的编译器可以做任何该死的事情,并且永远不需要告诉你,你正在做一些实现定义的行为.这导致人们编写危险的非便携代码,这些代码适用于某些编译器,并在其他编译器上执行完全不同的操作. (21认同)
  • 我必须说,对于真正的好奇,这是一个很好的知识,但对于普通的C#应用​​程序,其他答案中的措辞和实际发生的事情之间的差异远远低于它真正使用的语言的抽象级别没有不同.C#不是汇编程序,在代码中使用了99.9%的`i ++'或`++ i`,后台发生的事情就是这样;*在后台*.我编写C#来爬升到这个级别上的抽象级别,所以如果这对你的C#代码非常重要,你可能已经使用了错误的语言. (14认同)
  • +1,但我必须说实话......多年来我使用后缀运算符的唯一用途并不是单独行(因此不是`i ++;`)在`for(int i = 0;我<x; i ++)`...而且我对此感到非常高兴!(我从来没有使用前缀运算符).如果我必须写一些需要高级程序员2分钟才能破译的内容......嗯......最好再编写一行代码或引入一个临时变量:-)我认为你的"文章"(我赢了)不称它为"答案")证明我的选择:-) (11认同)
  • +1:对于真正好奇的人,您能否参考"C中这些操作的设计缺陷"的含义? (4认同)
  • 非常有帮助和有趣。我在“剩余操作之前/之后完成增量”阵营。我现在已经看到了我的方式的错误。谢谢!! (2认同)

Kie*_*one 185

奇怪的是,看起来其他两个答案并没有拼出来,这绝对值得说:


i++意思是'告诉我价值i,然后增加'

++i意思是"递增i,然后告诉我价值"


它们是预增量,后增量运算符. 在这两种情况下,变量都会递增,但如果要在完全相同的情况下获取两个表达式的值,则结果会有所不同.

  • 这两种说法都不正确.考虑你的第一个陈述.i ++实际上意味着"保存值,增加值,将其存储在i中,然后告诉我原始保存的值".也就是说,告诉发生在*之后*递增,而不是*之前*正如你所说的那样.考虑第二个陈述.i ++实际上意味着"保存值,增加值,将其存储在i中,并告诉我递增的值".你说的方式不清楚这个值是i的值,还是分配给i的值;*它们可能不同*. (64认同)
  • 当然,*大部分时间*描述操作语义的草率,不正确的方式给出与精确和正确描述相同的结果.首先,我没有看到任何引人注目的*值*通过不正确的推理得到正确的答案,其次,是的,我看到生产代码正是这种错误.我每年可能会从真正的程序员那里得到六个问题,关于为什么某些特定的表达式充满了增量和减量以及数组解引用的方式并不像他们假设的那样有效. (20认同)
  • @supercat:讨论是关于C#,而不是C. (12认同)
  • 这似乎与[埃里克所说的]不一致(http://stackoverflow.com/questions/3346450/c-what-is-the-difference-between-i-and-i/3346729#3346729) (10认同)
  • @Eric,在我看来甚至你的答案,第二个声明是绝对正确的.虽然他排除了几步. (7认同)
  • @KierenJohnstone下面的代码示例显示了在`i`递增之前声称`a`是"告诉`i`"的值如何在`a`上产生错误的印象:`int i = int.MaxValue; int a = 0; try {checked {a = i ++; catch {Console.WriteLine(a); 输出是'0',因为```不是***告诉***任何东西,直到`i`递增.增加`i`抛出一个异常,`a`保持不变. (7认同)
  • @MilanGardian我回滚了你的编辑.对或错,您的编辑改变了答案,这应该是OP的决定. (5认同)
  • 我投了票,我不是盲目的.你的答案是不正确的.i ++和++我以相同的顺序转换为相同的事件除了在表达式求值结束时返回的内容.你的答案似乎暗示不然.如果您还不清楚,请重新阅读Eric的答案. (4认同)
  • 无论其他人说什么,这都是完全正确的.就生成的代码而言,即使Eric也是错误的.而他和其他人都假装Kierin表示增量发生在表达的其余部分之后.他没有说出任何他说的话. (4认同)
  • Eric,感谢您的评论,但我看不出任何情况,在我说的和物理底层流程之间的真实C#代码示例(即使在审查中)会有明显的差异? (2认同)
  • @Brian - 没有适用的情况 - 我说的不是真的.正如我所说,我不是说"告诉我i的价值,然后评估其余的陈述,然后增加":我们说的是同样的事情,但我的回答更简洁! (2认同)

dcp*_*dcp 23

如果你有:

int i = 10;
int x = ++i;
Run Code Online (Sandbox Code Playgroud)

然后x11.

但如果你有:

int i = 10;
int x = i++;
Run Code Online (Sandbox Code Playgroud)

然后x10.

请注意,Eric指出,在两种情况下,增量同时发生,但它是不同的结果给出的值(感谢Eric!).

一般来说,我喜欢使用,++i除非有充分的理由不这样做.例如,在编写循环时,我喜欢使用:

for (int i = 0; i < 10; ++i) {
}
Run Code Online (Sandbox Code Playgroud)

或者,如果我只需要增加一个变量,我喜欢使用:

++x;
Run Code Online (Sandbox Code Playgroud)

通常情况下,这种方式或其他方式没有多大意义并且归结为编码风格,但如果您在其他任务中使用运算符(例如在我的原始示例中),那么了解潜在的副作用很重要.

  • 这个答案是危险的错误.**当增量发生时,相对于剩余的操作没有改变**所以说在一种情况下它在*剩余操作之前完成*而在另一种情况下它在*剩余操作之后完成*是非常误导的.*在两种情况下,增量都是同时进行的*.不同的是*作为结果*给出什么值,而不是*当增量完成*时. (21认同)
  • 我编辑了这个,使用`i`作为变量名而不是`var`,因为它是一个C#关键字. (3认同)
  • 您可以通过按顺序放置代码行来澄清您的示例 - 也就是说,"var x = 10; var y = ++ x;" 和"var x = 10; var y = x ++;" 代替. (2认同)
  • 所以没有分配变量,只是说"i ++;" 或"++ i;" 会给出完全相同的结果还是我弄错了? (2认同)

the*_*ost 6

int i = 0;
Console.WriteLine(i++); // Prints 0. Then value of "i" becomes 1.
Console.WriteLine(--i); // Value of "i" becomes 0. Then prints 0.
Run Code Online (Sandbox Code Playgroud)

这回答了你的问题了吗 ?

  • 这似乎是回答这个问题的最不明确的方式. (43认同)
  • +1有创意.-1表示有创意. (17认同)
  • 看起来类似的事情发生在我的选票上:他们起起落落......所以我坚持0 :-) (5认同)
  • 可以想象,后缀增量和前缀减量对变量没有任何影响:P (4认同)
  • 哦,*那是*聪明! (2认同)

Mik*_*ebb 5

运算符的工作方式是它同时递增,但如果它在变量之前,则表达式将使用递增/递减的变量进行求值:

int x = 0;   //x is 0
int y = ++x; //x is 1 and y is 1
Run Code Online (Sandbox Code Playgroud)

如果它在变量之后,当前语句将使用原始变量执行,就好像它还没有递增/递减一样:

int x = 0;   //x is 0
int y = x++; //'y = x' is evaluated with x=0, but x is still incremented. So, x is 1, but y is 0
Run Code Online (Sandbox Code Playgroud)

除非必要,否则我同意dcp使用预增量/减量(++ x).真的是我使用后递增/递减的唯一时间是while循环或那种循环.这些循环是相同的:

while (x < 5)  //evaluates conditional statement
{
    //some code
    ++x;       //increments x
}
Run Code Online (Sandbox Code Playgroud)

要么

while (x++ < 5) //evaluates conditional statement with x value before increment, and x is incremented
{
    //some code
}
Run Code Online (Sandbox Code Playgroud)

你也可以在索引数组时这样做:

int i = 0;
int[] MyArray = new int[2];
MyArray[i++] = 1234; //sets array at index 0 to '1234' and i is incremented
MyArray[i] = 5678;   //sets array at index 1 to '5678'
int temp = MyArray[--i]; //temp is 1234 (becasue of pre-decrement);
Run Code Online (Sandbox Code Playgroud)

等等......


小智 5

只是为了记录,在 C++ 中,如果您可以使用(即)您不关心操作的顺序(您只想递增或递减并稍后使用它),前缀运算符更有效,因为它没有必须创建对象的临时副本。不幸的是,大多数人使用 posfix (var++) 而不是 prefix (++var),因为这是我们最初学到的。(我在一次采访中被问到这个问题)。不确定这在 C# 中是否正确,但我认为是这样。

  • 这就是为什么我在 c# 中使用 ++i 来单独使用(即不在更大的表达式中)。每当我看到带有 i++ 的 ac# 循环时,我都会流泪,但现在我知道在 c# 中这实际上是几乎相同的代码,我感觉好多了。就个人而言,我仍然会使用 ++i,因为习惯很难改掉。 (2认同)