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

1155
在C语言中,使用++ii++的区别是什么?在for循环的增量块中应该使用哪个?

25
不确定原帖作者是否感兴趣,但在 C++ 中,性能差异可能会很大,因为创建临时对象对于用户定义的类型来说可能是昂贵的。 - On Freund
20个回答

1416
  • ++i将会增加i的值,并返回增加后的值。

  • ++i将会增加i的值,并返回增加后的值。

  •  i = 1;
     j = ++i;
     (i is 2, j is 2)
    
  • i++ 会将 i 的值加一,但会返回在自增之前 i 的原始值。

  •  i = 1;
     j = i++;
     (i is 2, j is 1)
    

    对于一个for循环,两者都可以使用。 ++i 更常用,可能是因为它在K&R中被使用。

    不管怎样,按照“比起i++,更倾向于++i”的指导方针就不会出错。

    关于++ii++效率的问题有一些评论。在任何非学生项目的编译器中,它们之间没有性能差异。您可以通过查看生成的代码来验证这一点,这两者将是相同的。

    效率问题很有趣……以下是我的回答尝试:C语言中的i++和++i是否有性能差异?

    正如@OnFreund所指出的,对于C++对象来说情况是不同的,因为operator++()是一个函数,编译器无法知道如何优化创建临时对象以保存中间值。


    8
    到达结束条件时,这是否会影响循环再次运行的情况?例如,for(int i=0; i<10; i++){ print i; }for(int i=0; i<10; ++i){ print i; } 是否有所不同?我理解有些语言将根据你使用的方式给出不同的结果。 - aVeRTRAC
    41
    Jonnyflash,两者的操作是相同的,因为i的增量和打印在不同的语句中。对于任何支持C样式++的语言,情况都应该如此。++i和i++之间的唯一区别将在同一语句中使用操作的值时出现。 - Mark Harrison
    21
    由于它们在大多数情况下生成相同的代码,我更喜欢使用i++,因为它的形式是“操作数-运算符”,类似于赋值语句中的“操作数-运算符-值”。换句话说,目标操作数位于表达式的左侧,就像在赋值语句中一样。 - David R Tribble
    6
    @MarkHarrison,它的操作方式是相同的,不是因为i++print i在不同的语句中,而是因为i++;i<10是不同的。@jonnyflash的评论并没有太离谱。假设你有for(int i=0; i++<10){ print i; }for(int i=0; ++i<10){ print i; }。这些将会以@johnnyflash在第一个评论中描述的方式有所不同。 - Adam
    3
    @sam,因为在典型的for循环中,在++i这一部分没有副作用(比如赋值)发生。 - Mark Harrison
    显示剩余9条评论

    242

    i++被称为后增量,而++i被称为前增量

    i++

    i++是后增量,因为它在操作结束后将i的值增加1。

    让我们看下面的例子:

    int i = 1, j;
    j = i++;
    

    这里j = 1,但i = 2。这里i的值将首先赋给j,然后i将被递增。

    ++i

    ++i是前增量,因为它在操作之前将i的值增加1。 这意味着j = i;将在i++之后执行。

    让我们看下面的例子:

    int i = 1, j;
    j = ++i;
    

    这里的j = 2但是i = 2。在i递增之后,i的值将被赋给j。 同样地,在j=i;之前,++i会被执行。
    对于你的问题在for循环的递增块中应该使用哪个?答案是,你可以使用任何一个... 没关系。它会执行相同次数的for循环。
    for(i=0; i<5; i++)
       printf("%d ", i);
    

    for(i=0; i<5; ++i)
       printf("%d ", i);
    

    两个循环将产生相同的输出,即 0 1 2 3 4
    只有在使用它的地方才会有所区别。
    for(i = 0; i<5;)
        printf("%d ", ++i);
    

    在这种情况下,输出将是1 2 3 4 5

    61

    i++:在这种情况下,首先赋值,然后进行递增。

    ++i:在这种情况下,首先进行递增,然后赋值。

    以下是图像可视化,同时这里有一个很好的实用视频,演示了相同的内容。

    enter image description here


    1
    你如何对未分配的变量进行递增? - kouty
    1
    @kouty 你可以对未分配给变量的寄存器进行递增操作。 - Polluks
    你可以在不进行初始赋值的情况下递增数字。比如让 i = 0,nums[++i]。 - Front-end Developer

    55

    ++i 会增加变量的值并返回增加后的结果。

    i++ 会先返回变量的值,然后再增加它。

    这是一个微妙的区别。

    对于for循环,使用++i,因为它稍微更快一些。i++会创建一个额外的副本然后被丢弃掉。


    30
    至少就整数而言,我不知道有任何编译器会产生差异。 - blabla999
    14
    不会更快。值将被忽略(只有副作用有效),编译器可以/将生成完全相同的代码。 - wildplasser

    50

    请不要担心哪一个更"高效"(其实指的是速度)的问题。我们现在有编译器来处理这些事情。基于哪一个能更清晰地表达你的意图,请使用其中之一。


    3
    我会尽力进行翻译。需要翻译的内容是:which, I would hope, means 'use prefix (inc|dec)rement unless you actually need the old value prior to the (inc|dec), which very few people do, and yet which a bewildering proportion of supposed teaching materials use, creating a cargo cult of postfix users who don't even know what it is'..!我希望这意味着“在你实际需要(inc|dec)rement之前的旧值时,使用前缀而不是后缀,而很少有人这样做,但令人困惑的是,许多所谓的教材使用后缀,创造出一种后缀用户的迷信群体,他们甚至不知道它是什么”。 - underscore_d
    3
    我不确定 "现今的编译器会自动处理这些事情" 在所有情况下都是正确的。在自定义的 operator++(int)(后缀版本)中,代码几乎必须创建一个临时变量,以便返回它。您确定编译器总是能够优化掉这种情况吗? - Peter - Reinstate Monica
    1
    过早的优化如果增加了复杂性,那么是有害的。然而,对于哪个更快并使用它感到好奇不会增加复杂性。这是对语言好奇,应该得到奖励。从概念上讲,说“添加一个并使用它”比“保存在其他地方,添加一个并返回保存的东西”更清晰。“++i”在速度和风格上更可取。此外,一个C的学生转向C++,如果他将“i++”写在编译器无法删除的复杂类型上,则可能喜欢被教授使用它。 - user904963

    46

    唯一的区别是变量递增和操作符返回值之间的顺序不同。

    此代码及其输出解释了这种差异:

    #include<stdio.h>
    
    int main(int argc, char* argv[])
    {
      unsigned int i=0, a;
      printf("i initial value: %d; ", i);
      a = i++;
      printf("value returned by i++: %d, i after: %d\n", a, i);
      i=0;
      printf("i initial value: %d; ", i);
      a = ++i;
      printf(" value returned by ++i: %d, i after: %d\n",a, i);
    }
    

    输出结果为:

    i initial value: 0; value returned by i++: 0, i after: 1
    i initial value: 0;  value returned by ++i: 1, i after: 1
    

    基本上,++i 在增加之后返回值,而 i++ 在增加之前返回值。最终,在两种情况下,i 的值都会增加。

    另一个例子:

    #include<stdio.h>
    
    int main ()
      int i=0;
      int a = i++*2;
      printf("i=0, i++*2=%d\n", a);
      i=0;
      a = ++i * 2;
      printf("i=0, ++i*2=%d\n", a);
      i=0;
      a = (++i) * 2;
      printf("i=0, (++i)*2=%d\n", a);
      i=0;
      a = (i++) * 2;
      printf("i=0, (i++)*2=%d\n", a);
      return 0;
    }
    

    输出:

    i=0, i++*2=0
    i=0, ++i*2=2
    i=0, (++i)*2=2
    i=0, (i++)*2=0
    

    很多时候没有区别

    当返回值被赋给另一个变量时,或者在与其他操作连接拼接并应用操作优先级的情况下进行增量操作时,区别是明显的 (i++*2不同于++i*2,以及(i++)*2(++i)*2)。在许多情况下,它们是可以互换的。一个经典的例子是for循环的语法:

    for(int i=0; i<10; i++)
    

    具有相同的效果

    for(int i=0; i<10; ++i)
    

    效率

    预增量操作始终至少与后增量操作一样有效:事实上,后增量操作通常涉及保留先前值的副本并可能添加一些额外的代码。

    正如其他人所建议的那样,由于编译器优化,它们许多次是同样有效的,可能会在for循环中出现这些情况。

    记住的规则

    为了不混淆这两个运算符,我采用了这个规则:

    将操作符 ++ 相对于变量 i 的位置与 ++ 操作相对于分配顺序相关联

    换句话说:

    • ++ 之前 i 表示必须在分配之前进行递增;
    • ++ 之后 i 表示必须在分配之后进行递增:

    1
    '(i++)*2和(++i)*2返回相同的值'确定吗? - Yusuf R. Karagöz
    @YusufR.Karagöz,你说得完全正确,感谢指出。 - roschach
    1
    1. 示例输出也需要修正。
    - Yusuf R. Karagöz

    29

    ++ii++执行速度稍快,原因在于i++在增加i之前可能需要创建i的一个本地副本,而++i从不这样做。在某些情况下,一些编译器将尝试优化此过程...但并非总是可行,也并非所有编译器都这样做。

    我尽量不过度依赖编译器的优化,所以我会遵循Ryan Fox的建议:当可以使用两者时,我使用++i


    17
    -1 对于 C 语言问题的 C++ 回答。当你写下语句 1; 时,变量 i 的值就像数字 1 一样,没有更多的“本地副本”。 - R.. GitHub STOP HELPING ICE

    20
    使用i++和++i两种方式在循环中的结果是完全相同的。换句话说,在这两种情况下,循环将执行完全相同的操作。
    从效率方面考虑,选择i++可能会有一定的惩罚。根据语言规范,使用后缀递增操作符应该会在操作符所作用的值上创建一个额外的副本,这可能会导致额外的操作。
    然而,需要考虑到前述逻辑存在两个主要问题。
    1. 现代编译器非常出色。所有优秀的编译器都足够聪明,能够意识到它正在看到for循环中的整数递增操作,并且会将两种方法优化为相同的高效代码。如果使用后缀递增比前缀递增导致程序运行时间更慢,那么你正在使用一个糟糕的编译器。

    2. 在操作时间复杂度方面,两种方法(即使实际上执行了复制操作)是等效的。在循环内部执行的指令数量应该远远超过递增操作中的操作数。因此,在任何规模较大的循环中,递增方法的惩罚都会被循环体的执行大大超越。换句话说,与其担心递增操作,你更应该关注优化循环中的代码。

    在我看来,整个问题只是一个风格偏好。如果您认为前缀递增更可读,则使用前缀递增。我个人更喜欢后缀递增,但这可能是因为在了解任何有关优化的知识之前,我就学会了它。
    这是过早地进行优化的典型例子,而这样的问题有可能让我们分散注意力,从而无法解决设计上的严重问题。然而,询问此类问题仍然是一个好习惯,因为在使用方法或“最佳实践”方面并没有一致性。

    11

    ++i:是前缀自增,另一个是后缀自增。

    i++:先获取元素再将其递增。
    ++i:递增 i 并返回元素。

    示例:

    int i = 0;
    printf("i: %d\n", i);
    printf("i++: %d\n", i++);
    printf("++i: %d\n", ++i);
    

    输出:

    i: 0
    i++: 0
    ++i: 2
    

    10

    ++i(前缀操作):先增加值再赋值
    例如:int i = 5int b = ++i 在这种情况下,先将6赋值给b,然后增加为7,以此类推。

    i++(后缀操作):先赋值再增加值
    例如:int i = 5int b = i++ 在这种情况下,先将5赋值给b,然后增加为6,以此类推。

    在for循环中,通常使用i++,因为我们通常在增加前使用i的起始值。但根据程序逻辑可能会有所不同。


    2
    最后一句话似乎是错误的,++i和i++在for循环中的作用方式是相同的,但你的句子暗示了不同的情况。 - Andris

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接