【问题标题】:What is the difference between ++i and i++?++i 和 i++ 有什么区别?
【发布时间】:2021-08-25 21:24:03
【问题描述】:

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

【问题讨论】:

  • 不确定原始发布者是否感兴趣,但在 C++ 中,性能差异可能很大,因为创建临时对象对于用户定义的类型来说可能代价高昂。

标签: c for-loop post-increment pre-increment


【解决方案1】:

您可以将其内部转换视为多个语句

// case 1

i++;

/* you can think as,
 * i;
 * i= i+1;
 */



// case 2

++i;

/* you can think as,
 * i = i+i;
 * i;
 */

【讨论】:

【解决方案2】:

唯一的区别是变量的增量和运算符返回的值之间的运算顺序。

这段代码及其输出解释了区别:

#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=2

很多时候没有区别

将返回值分配给另一个变量或与应用操作优先级的其他操作串联执行递增时,差异很明显(i++*2++i*2 不同,但(i++)*2(++i)*2返回相同的值)在许多情况下它们是可以互换的。一个经典的例子是 for 循环语法:

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

效果和

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

要记住的规则

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

将运算符++相对于变量i的位置与++运算相对于赋值的顺序相关联

换句话说:

  • ++ before i 表示递增必须在before赋值;
  • ++ after i 表示必须在赋值之后进行递增:

【讨论】:

    【解决方案3】:

    在循环中使用两者的有效结果是相同的。换句话说,循环在两种情况下都会做同样的事情。

    就效率而言,选择 i++ 而不是 ++i 可能会带来损失。就语言规范而言,使用后自增运算符应创建运算符所作用值的额外副本。这可能是额外操作的来源。

    但是,您应该考虑上述逻辑的两个主要问题。

    1. 现代编译器很棒。所有好的编译器都足够聪明,可以意识到它在 for 循环中看到了一个整数增量,并且它将两种方法优化为相同的高效代码。如果使用后增量而不是前增量实际上会导致您的程序运行时间变慢,那么您使用的是糟糕编译器。

    2. 就操作时间复杂度而言,这两种方法(即使实际上正在执行复制)是等效的。在循环内部执行的指令数量应显着支配增量操作中的操作数量。因此,在任何显着大小的循环中,增量方法的惩罚将被循环体的执行所掩盖。换句话说,你最好不要担心优化循环中的代码而不是增量。

    在我看来,整个问题归结为一种风格偏好。如果您认为预增量更具可读性,请使用它。就我个人而言,我更喜欢后增量,但这可能是因为这是我在对优化一无所知之前就被教过的。

    这是过早优化的典型示例,此类问题可能会分散我们对设计中严重问题的注意力。然而,这仍然是一个很好的问题,因为在“最佳实践”中没有统一的用法或共识。

    【讨论】:

      【解决方案4】:
      • ++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++ 的效率有几个cmets。在任何非学生项目编译器中,不会有性能差异。您可以通过查看生成的代码来验证这一点,这将是相同的。

      效率问题很有趣......这是我尝试回答的问题: Is there a performance difference between i++ and ++i in C?

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

      【讨论】:

      • 这种效果不会影响循环在达到结束条件时再次运行吗?例如,for(int i=0; i&lt;10; i++){ print i; } 不会与 for(int i=0; i&lt;10; ++i){ print i; } 不同。我的理解是,根据您使用的语言,某些语言会给出不同的结果。
      • jonnyflash,两者的操作方式相同,因为 i 的增量和打印在不同的语句中。任何支持 C 风格 ++ 的语言都应该是这种情况。 ++i 和 i++ 之间的唯一区别是在同一语句中使用操作的值。
      • 由于在大多数情况下它们产生相同的代码,我更喜欢i++,因为它的形式是“操作数-操作符”,也就是赋值“操作数-操作符-值”。换句话说,目标操作数在表达式的左侧,就像在赋值语句中一样。
      • @MarkHarrison,它将以相同的方式运行,不是因为i++print i 在不同的语句中,而是因为i++;i&lt;10 是。 @jonnyflash 的评论并不是那么离谱。假设您有for(int i=0; i++&lt;10){ print i; }for(int i=0; ++i&lt;10){ print i; }。这些将按照@johnnyflash 在第一条评论中描述的方式以不同的方式运行。
      • @sam,因为在典型的 for 循环中,++i 部分没有副作用(例如赋值)。
      【解决方案5】:

      i++:在这种情况下,首先分配值,然后发生增量。

      ++i:在这种情况下,首先完成增量,然后分配值

      下面是图像可视化和here is a nice practical video 演示。

      【讨论】:

      • 如何增加一些未分配的?
      • @kouty 您可以增加未分配给变量的寄存器。
      • 您可以在不分配初始值的情况下增加数字。例如让 i = 0, nums[++i].
      【解决方案6】:

      很快:

      ++ii++ 如果您没有将它们写在函数中,它们的工作方式相同。如果你使用 function(i++)function(++i) 之类的东西,你会看到不同之处。

      function(++i) 表示首先将 i 加 1,然后将此 i 放入具有新值的函数中。

      function(i++) 表示先将 i 放入函数中,然后将 i 增加 1。

      int i=4;
      printf("%d\n",pow(++i,2));//it prints 25 and i is 5 now
      i=4;
      printf("%d",pow(i++,2));//it prints 16 i is 5 now
      

      【讨论】:

      • 差异与函数调用并没有真正的联系(您可以在不进行函数调用的情况下发现差异)。即使不涉及函数调用,int j = ++i;int k = i++; 之间也存在差异。
      【解决方案7】:

      ++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
      

      【讨论】:

        【解决方案8】:

        ++i(前缀操作):递增然后赋值
        (例如):int i = 5int b = ++i 在这种情况下,首先将 6 分配给 b,然后递增到 7,依此类推。

        i++(后缀操作):赋值然后递增值
        (例如):int i = 5int b = i++ 在这种情况下,首先将 5 分配给 b,然后递增到 6,依此类推。

        for 循环的情况:i++ 最常用,因为通常我们在 for 循环递增之前使用i 的起始值。但根据您的程序逻辑,它可能会有所不同。

        【讨论】:

        • 最后一条语句似乎是错误的,++i 和 i++ 在 for 循环中的工作方式相同,但您的句子表明并非如此。
        【解决方案9】:

        Pre-crement 表示在同一行上递增。后增量是指行执行后增量。

        int j=0;
        System.out.println(j); //0
        System.out.println(j++); //0. post-increment. It means after this line executes j increments.
        
        int k=0;
        System.out.println(k); //0
        System.out.println(++k); //1. pre increment. It means it increments first and then the line executes
        

        当它带有 OR、AND 运算符时,它变得更加有趣。

        int m=0;
        if((m == 0 || m++ == 0) && (m++ == 1)) { //false
        /* in OR condition if first line is already true then compiler doesn't check the rest. It is technique of compiler optimization */
        System.out.println("post-increment "+m);
        }
        
        int n=0;
        if((n == 0 || n++ == 0) && (++n == 1)) { //true
        System.out.println("pre-increment "+n); //1
        }
        

        在数组中

        System.out.println("In Array");
        int[] a = { 55, 11, 15, 20, 25 } ;
        int ii, jj, kk = 1, mm;
        ii = ++a[1]; // ii = 12. a[1] = a[1] + 1
        System.out.println(a[1]); //12
        
        jj = a[1]++; //12
        System.out.println(a[1]); //a[1] = 13
        
        mm = a[1];//13
        System.out.printf ( "\n%d %d %d\n", ii, jj, mm ) ; //12, 12, 13
        
        for (int val: a) {
             System.out.print(" " +val); //55, 13, 15, 20, 25
        }
        

        在 C++ 中指针变量的后/前增量

        #include <iostream>
        using namespace std;
        
        int main() {
        
            int x=10;
            int* p = &x;
        
            std::cout<<"address = "<<p<<"\n"; //prints address of x
            std::cout<<"address = "<<p<<"\n"; //prints (address of x) + sizeof(int)
            std::cout<<"address = "<<&x<<"\n"; //prints address of x
        
            std::cout<<"address = "<<++&x<<"\n"; //error. reference can't re-assign because it is fixed (immutable)
        }
        

        【讨论】:

          【解决方案10】:

          以下 C 代码片段说明了前后递增和递减运算符之间的区别:

          int  i;
          int  j;
          

          增量运算符:

          i = 1;
          j = ++i;    // i is now 2, j is also 2
          j = i++;    // i is now 3, j is 2
          

          【讨论】:

            【解决方案11】:

            他们都增加了数字。 ++i 等价于i = i + 1

            i++++i 非常相似,但并不完全相同。两者都增加数字,但++i 在计算当前表达式之前增加数字,而i++ 在计算表达式后增加数字。

            例子:

            int i = 1;
            int x = i++; //x is 1, i is 2
            int y = ++i; //y is 3, i is 3
            

            【讨论】:

              【解决方案12】:

              i++ 和 ++i

              这个小代码可能有助于从与已发布答案不同的角度可视化差异:

              int i = 10, j = 10;
                
              printf ("i is %i \n", i);
              printf ("i++ is %i \n", i++);
              printf ("i is %i \n\n", i);
                
              printf ("j is %i \n", j);
              printf ("++j is %i \n", ++j);
              printf ("j is %i \n", j);
              

              结果是:

              //Remember that the values are i = 10, and j = 10
              
              i is 10 
              i++ is 10     //Assigns (print out), then increments
              i is 11 
              
              j is 10 
              ++j is 11    //Increments, then assigns (print out)
              j is 11 
              

              注意前后情况。

              for循环

              至于哪一个应该用在for循环的增量块中,我认为我们能做的最好的决定就是使用一个很好的例子:

              int i, j;
              
              for (i = 0; i <= 3; i++)
                  printf (" > iteration #%i", i);
              
              printf ("\n");
              
              for (j = 0; j <= 3; ++j)
                  printf (" > iteration #%i", j);
              

              结果是:

              > iteration #0 > iteration #1 > iteration #2 > iteration #3
              > iteration #0 > iteration #1 > iteration #2 > iteration #3 
              

              我不了解你,但我看不出它的用法有什么不同,至少在 for 循环中是这样。

              【讨论】:

                【解决方案13】:

                a=i++ 表示 a 包含当前 i 值 a=++i 表示 a 包含递增的 i 值

                【讨论】:

                • 这个答案不准确。 a = i++; 表示a 中存储的值将是递增前i 的值,但“不递增”意味着i 不递增,这是完全错误的——i 递增,但是表达式的值是增量之前的值。
                【解决方案14】:

                主要区别是

                • i++ Post(增量后)和
                • ++i Pre(增量前

                  • 如果i =1 循环增加像1,2,3,4,n
                  • pre if i =1 循环增量如 2,3,4,5,n

                【讨论】:

                  【解决方案15】:

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

                  i++

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

                  让我们看看下面的例子:

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

                  这里的值为j = 1i = 2。这里i 的值将首先分配给j,然后i 将递增。

                  ++i

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

                  让我们看看下面的例子:

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

                  这里的值为j = 2i = 2。这里i 的值将在i 递增i 之后分配给j。 同样++i会在j=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

                  【讨论】:

                    【解决方案16】:

                    可以通过下面这个简单的 C++ 代码来理解区别:

                    int i, j, k, l;
                    i = 1; //initialize int i with 1
                    j = i+1; //add 1 with i and set that as the value of j. i is still 1
                    k = i++; //k gets the current value of i, after that i is incremented. So here i is 2, but k is 1
                    l = ++i; // i is incremented first and then returned. So the value of i is 3 and so does l.
                    cout << i << ' ' << j << ' ' << k << ' '<< l << endl;
                    return 0;
                    

                    【讨论】:

                      【解决方案17】:

                      这是理解差异的示例

                      int i=10;
                      printf("%d %d",i++,++i);
                      

                      输出:10 12/11 11(取决于 printf 函数的参数求值顺序,因编译器和架构而异)

                      说明: i++->i 被打印出来,然后递增。 (打印 10,但 i 将变为 11) ++i->i 值递增并打印该值。 (打印 12,i 的值也是 12)

                      【讨论】:

                      • 这会导致未定义的行为,因为i++++i 之间没有序列点
                      【解决方案18】:

                      ++i 可以i++ 稍快的原因是i++ 可能需要 i 的值的本地副本才能增加,而 ++i 永远不会。在某些情况下,如果可能,一些编译器会对其进行优化……但这并不总是可能的,而且并非所有编译器都这样做。

                      我尽量不要过多地依赖编译器优化,所以我会听从 Ryan Fox 的建议:当我可以同时使用两者时,我会使用 ++i

                      【讨论】:

                      • -1 表示 C++ 对 C 问题的回答。当您编写语句1; 时,i 的值的“本地副本”不比值 1 多。
                      【解决方案19】:

                      ++i 增加值,然后返回它。

                      i++ 返回值,然后递增。

                      这是一个微妙的区别。

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

                      【讨论】:

                      • 我不知道任何编译器至少对整数有影响。
                      • 并不快。这些值被忽略(只有副作用有效),编译器可以/将生成完全相同的代码。
                      【解决方案20】:

                      请不要担心哪个更快的“效率”(实际上是速度)。这些天我们有编译器来处理这些事情。使用任何一个有意义的,根据哪个更清楚地表明你的意图。

                      【讨论】:

                      • 我希望这意味着'使用前缀 (inc|dec)rement,除非您实际上需要 (inc|dec) 之前的旧值,而很少有人这样做,并且然而,其中一个令人眼花缭乱的假设教材使用,创造了一个甚至不知道它是什么的 postfix 用户的货物崇拜'..!
                      • 我不确定“现在的编译器......照顾这些事情”是否普遍正确。在自定义operator++(int)(后缀版本)中,代码几乎必须创建一个将返回的临时代码。你确定编译器总是可以优化它吗?
                      • 过早的优化如果增加了复杂性是有害的。但是,对哪个更快并使用它感到好奇并不会增加复杂性。这是对语言的好奇心,应该得到回报。说“添加一个并使用它”比“将它保存在其他地方,添加一个,然后返回保存的那个”在概念上也感觉更清晰。 ++i 在速度和风格上可能更受欢迎。此外,如果他在编译器无法删除的复杂类型上写了i++,可能会喜欢向他教授 C++ 的 C 学生。
                      【解决方案21】:

                      我假设您现在了解语义上的差异(尽管老实说我想知道为什么 人们在堆栈溢出而不是阅读时询问“操作员 X 是什么意思”的问题, 你知道,一本书或网络教程或其他东西。

                      但无论如何,就使用哪一个而言,忽略性能问题,这些问题是 即使在 C++ 中也不太重要。这是您在决定时应该使用的原则 使用哪个:

                      在代码中说出你的意思。

                      如果您的语句中不需要 value-before-increment,请不要使用这种形式的运算符。这是一个小问题,但除非您使用的是禁止使用的样式指南 完全支持另一个版本(又名愚蠢的风格指南),您应该使用 最准确地表达你想要做什么的形式。

                      QED,使用预增量版本:

                      for (int i = 0; i != X; ++i) ...
                      

                      【讨论】:

                        猜你喜欢
                        • 2011-03-21
                        • 2021-12-30
                        相关资源
                        最近更新 更多