在 C 中,使用 ++i
和 i++
有什么区别,应该在 for
循环的增量块中使用哪个?
++i 将增加 i 的值,然后返回增加的值。我 = 1; j = ++i; (我是 2,j 是 2)
i++ 将增加 i 的值,但返回 i 在增加之前持有的原始值。我 = 1; j = i++; (i 是 2,j 是 1)
对于 for
循环,两者都有效。 ++i
似乎更常见,可能是因为 K&R 中使用了它。
无论如何,请遵循“首选 ++i
而不是 i++
”的指导方针,您不会出错。
关于 ++i
和 i++
的效率有几条评论。在任何非学生项目编译器中,不会有性能差异。您可以通过查看生成的代码来验证这一点,这将是相同的。
效率问题很有趣......这是我尝试回答的问题:Is there a performance difference between i++ and ++i in C?
正如 @OnFreund 所指出的,它对于 C++ 对象是不同的,因为 operator++()
是一个函数,编译器无法知道优化临时对象的创建以保存中间值。
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
的 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
。
i++
:在这种情况下,首先分配值,然后发生增量。
++i
:在这种情况下,首先完成增量,然后分配值
下面是图像可视化以及演示相同内容的 here is a nice practical video。
https://i.stack.imgur.com/YTZO8.png
++i
增加值,然后返回它。
i++
返回值,然后将其递增。
这是一个微妙的区别。
对于 for 循环,使用 ++i
,因为它稍微快一些。 i++
将创建一个刚刚被丢弃的额外副本。
请不要担心哪个更快的“效率”(实际上是速度)。这些天我们有编译器来处理这些事情。使用任何一个有意义的,基于哪个更清楚地表明你的意图。
operator++(int)
(后缀版本)中,代码几乎必须创建一个将返回的临时文件。你确定编译器总是可以优化它吗?
++i
在速度和风格上可能更受欢迎。此外,如果他在编译器无法删除的复杂类型上编写 i++
,则可能会喜欢向他教授 C++ 的 C 学生。
唯一的区别是变量的增量和运算符返回的值之间的运算顺序。
此代码及其输出解释了差异:
#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 表示必须在赋值之前进行递增;
++ 在 i 之后表示必须在赋值后进行递增:
++i
可以 比 i++
稍快的原因是 i++
可能需要 i 的值的本地副本,然后才能递增,而 ++i
永远不会。在某些情况下,如果可能,一些编译器会对其进行优化……但这并不总是可能的,而且并非所有编译器都这样做。
我尽量不要过多地依赖编译器优化,所以我会遵循 Ryan Fox 的建议:当我可以同时使用两者时,我会使用 ++i
。
1;
时,没有比值 1 更多的 i
值的“本地副本”。
在循环中使用任何一个的有效结果是相同的。换句话说,循环在两种情况下都会做同样的事情。
就效率而言,选择 i++ 而不是 ++i 可能会受到影响。就语言规范而言,使用后自增运算符应创建运算符所作用值的额外副本。这可能是额外操作的来源。
但是,您应该考虑上述逻辑的两个主要问题。
现代编译器很棒。所有优秀的编译器都足够聪明,可以意识到它在 for 循环中看到了一个整数增量,并且它将两种方法优化为相同的高效代码。如果使用后增量而不是前增量实际上会导致您的程序运行时间变慢,那么您使用的是糟糕的编译器。就操作时间复杂度而言,这两种方法(即使实际上正在执行复制)是等效的。在循环内部执行的指令数量应显着支配增量操作中的操作数量。因此,在任何显着大小的循环中,增量方法的惩罚将被循环体的执行所掩盖。换句话说,你最好不要担心优化循环中的代码而不是增量。
在我看来,整个问题归结为一种风格偏好。如果您认为预增量更具可读性,请使用它。就个人而言,我更喜欢后增量,但这可能是因为这是我在对优化一无所知之前就被教过的。
这是过早优化的典型例子,像这样的问题有可能分散我们对设计中严重问题的注意力。然而,这仍然是一个很好的问题,因为在“最佳实践”中没有统一的用法或共识。
++i
(前缀操作):递增然后赋值
(例如):int i = 5
,int b = ++i
在这种情况下,先将 6 分配给 b,然后再递增到 7等等。
i++
(后缀操作):赋值然后递增值
(例如):int i = 5
、int b = i++
在这种情况下,先将 5 分配给 b,然后再递增到 6等等。
在 for 循环的情况下:i++
最常用,因为通常我们在 for 循环中递增之前使用 i
的起始值。但根据您的程序逻辑,它可能会有所不同。
++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
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 循环中是这样。
以下 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
不久:
如果您不在函数中编写 ++i
和 i++
,它们的工作方式相同。如果您使用 function(i++)
或 function(++i)
之类的东西,您会看到不同之处。
function(++i)
表示首先将 i 增加 1,然后将此 i
放入具有新值的函数中。
function(i++)
表示在将 i
增加 1 之后将第一个 i
放入函数中。
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++;
之间也存在差异。
我假设您现在了解语义上的差异(尽管老实说,我想知道为什么人们会问关于堆栈溢出的“运算符 X 是什么意思”的问题,而不是阅读书籍或网络教程之类的东西。
但无论如何,就使用哪一个而言,忽略性能问题,即使在 C++ 中也不太重要。这是您在决定使用哪个时应遵循的原则:
说出你在代码中的意思。
如果您的语句中不需要 value-before-increment,请不要使用这种形式的运算符。这是一个小问题,但除非您使用的样式指南禁止一个版本而完全支持另一个版本(又名愚蠢的样式指南),否则您应该使用最准确地表达您想要做的事情的形式。
QED,使用预增量版本:
for (int i = 0; i != X; ++i) ...
可以通过下面这个简单的 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;
主要区别是 i++ Post(After Increment) 和 ++i Pre (Before Increment) post 如果 i =1 循环增量如 1,2,3,4,n pre 如果 i =1 循环增量如 2,3, 4,5,n
简而言之,两者之间的区别在于步骤看下图。
https://i.stack.imgur.com/CA8tL.jpg
例子:
int i = 1;
int j = i++;
j
结果是 1
int i = 1;
int j = ++i;
j
结果是 2
注意: 在这两种情况下,i
值都是 2
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 the OR condition, if the first line is already true
// then the compiler doesn't check the rest. It is a
// 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 the address of x
std::cout << "address = " << p <<"\n"; // Prints (the address of x) + sizeof(int)
std::cout << "address = " << &x <<"\n"; // Prints the address of x
std::cout << "address = " << ++&x << "\n"; // Error. The reference can't reassign, because it is fixed (immutable).
}
您可以将其内部转换视为多个语句:
// case 1
i++;
/* you can think as,
* i;
* i= i+1;
*/
// case 2
++i;
/* you can think as,
* i = i+i;
* i;
*/
a=i++ 表示 a 包含当前 i 值。
a=++i 表示 a 包含递增的 i 值。
a = i++;
表示存储在 a
中的值将是增量之前的 i
的值,但“不增加”意味着 i
不增加,这是完全错误的 - i
增加了表达式的值是增量之前的值。
这是理解差异的示例
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
之间没有序列点
for(int i=0; i<10; i++){ print i; }
这与for(int i=0; i<10; ++i){ print i; }
没有什么不同我的理解是,某些语言会根据您使用的语言为您提供不同的结果。i++
,因为它的形式是“操作数-操作符”,即赋值“操作数-操作符-值”。换句话说,目标操作数在表达式的左侧,就像它在赋值语句中一样。i++
和print i
在不同的语句中,而是因为i++;
和i<10
是。 @jonnyflash 的评论并不是那么离谱。假设您有for(int i=0; i++<10){ print i; }
和for(int i=0; ++i<10){ print i; }
。这些将按照@johnnyflash 在第一条评论中描述的方式以不同的方式运行。