for循环中的++i和i++有区别吗?这仅仅是语法问题吗?
当前回答
在某些情况下,++i和i+1可能会给出不同的结果,-i, i - 1等也是如此。
这并不是因为递增和递减操作符的工作方式有缺陷,而是因为新程序员有时会忽略一个小事实。
根据经验,不要在数组的方括号内使用inc/dec。例如,我不会用arr[++ I]来代替arr[I + 1]。虽然两者得到的i值是一样的,但这里我们忽略了一些东西。
如果循环条件基于i的执行值,那么将arr[i + 1]替换为arr[++i]将导致错误。为什么?
假设i = 5,那么arr[i + 1]意味着arr[6],而arr[++i]虽然意味着arr[6],但也会将i的值改变为6,这可能不是我们想要做的事情。我们可能不想改变i的值,但由于一个简单的++/——操作符,我们改变了这个值。
所以在使用++/——操作符时要小心。
我希望我能使我的观点更容易理解。
其他回答
对于用户定义类型的i,这些操作符可以(但不应该)在循环索引上下文中具有有意义的不同语义,这可能(但不应该)影响所描述的循环的行为。
此外,在c++中,使用预增量形式(++i)通常是最安全的,因为它更容易优化。(斯科特·朗廷(Scott Langham)先我一步找到了这个花边新闻。诅咒你,斯科特)
如果在循环中不使用增量之后的值,则没有区别。
for (int i = 0; i < 4; ++i){
cout<<i;
}
for (int i = 0; i < 4; i++){
cout<<i;
}
这两个循环都输出0123。
但是当你在循环中使用自增/自减后的值时,区别就来了:
预增量循环:
for (int i = 0,k=0; i < 4; k=++i){
cout<<i<<" ";
cout<<k<<" ";
}
输出: 0 0 1 2 - 2 3个3
增量后循环:
for (int i = 0, k=0; i < 4; k=i++){
cout<<i<<" ";
cout<<k<<" ";
}
输出: 0 0 1 0 2 1 3 - 2
我希望通过比较输出可以清楚地看出差异。这里需要注意的是,递增/递减总是在for循环的末尾执行,因此结果可以解释。
Pre-increment ++i增加i的值,并计算为新的增量值。
int i = 3;
int preIncrementResult = ++i;
Assert( preIncrementResult == 4 );
Assert( i == 4 );
后增量i++增加i的值,并计算为原始的非增量值。
int i = 3;
int postIncrementResult = i++;
Assert( postIncrementtResult == 3 );
Assert( i == 4 );
在c++中,如果可以使用前递增,则通常是首选。
这是因为如果使用后增量,它可能要求编译器必须生成创建额外临时变量的代码。这是因为被递增的变量的前值和新值都需要保存在某个地方,因为它们可能在被求值的表达式的其他地方被需要。
因此,至少在c++中,可以有性能差异来指导您选择使用哪一种。
这主要只在被递增的变量是用户定义的具有重写++操作符的类型时才会出现问题。对于基本类型(int等),没有性能差异。但是,作为指导原则,坚持使用前增量操作符是值得的,除非确实需要后增量操作符。
这里还有一些讨论。
In C++ if you're using STL, then you may be using for loops with iterators. These mainly have overridden ++ operators, so sticking to pre-increment is a good idea. Compilers get smarter all the time though, and newer ones may be able to perform optimizations that mean there's no performance difference - especially if the type being incremented is defined inline in header file (as STL implementations often are) so that the compiler can see how the method is implemented and can then know what optimizations are safe to perform. Even so, it's probably still worth sticking to pre-increment because loops get executed lots of times and this means a small performance penalty could soon get amplified.
在其他语言(如c#)中,c++操作符不能重载,在性能上没有区别。在循环中用于使循环变量前进,前增量操作符和后增量操作符是等效的。
更正:在c#中重载c++是允许的。不过,与c++相比,在c#中,你不能独立地重载前版本和后版本。因此,我假设在c#中调用++的结果没有被赋值给变量或用作复杂表达式的一部分,那么编译器会将++的前后版本简化为执行等效的代码。
下面是一个Java-Sample和字节码,后增量和前增量显示字节码没有区别:
public class PreOrPostIncrement {
static int somethingToIncrement = 0;
public static void main(String[] args) {
final int rounds = 1000;
postIncrement(rounds);
preIncrement(rounds);
}
private static void postIncrement(final int rounds) {
for (int i = 0; i < rounds; i++) {
somethingToIncrement++;
}
}
private static void preIncrement(final int rounds) {
for (int i = 0; i < rounds; ++i) {
++somethingToIncrement;
}
}
}
现在对于字节码(javap -private -c PreOrPostIncrement):
public class PreOrPostIncrement extends java.lang.Object{
static int somethingToIncrement;
static {};
Code:
0: iconst_0
1: putstatic #10; //Field somethingToIncrement:I
4: return
public PreOrPostIncrement();
Code:
0: aload_0
1: invokespecial #15; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: sipush 1000
3: istore_1
4: sipush 1000
7: invokestatic #21; //Method postIncrement:(I)V
10: sipush 1000
13: invokestatic #25; //Method preIncrement:(I)V
16: return
private static void postIncrement(int);
Code:
0: iconst_0
1: istore_1
2: goto 16
5: getstatic #10; //Field somethingToIncrement:I
8: iconst_1
9: iadd
10: putstatic #10; //Field somethingToIncrement:I
13: iinc 1, 1
16: iload_1
17: iload_0
18: if_icmplt 5
21: return
private static void preIncrement(int);
Code:
0: iconst_0
1: istore_1
2: goto 16
5: getstatic #10; //Field somethingToIncrement:I
8: iconst_1
9: iadd
10: putstatic #10; //Field somethingToIncrement:I
13: iinc 1, 1
16: iload_1
17: iload_0
18: if_icmplt 5
21: return
}
在某些情况下,++i和i+1可能会给出不同的结果,-i, i - 1等也是如此。
这并不是因为递增和递减操作符的工作方式有缺陷,而是因为新程序员有时会忽略一个小事实。
根据经验,不要在数组的方括号内使用inc/dec。例如,我不会用arr[++ I]来代替arr[I + 1]。虽然两者得到的i值是一样的,但这里我们忽略了一些东西。
如果循环条件基于i的执行值,那么将arr[i + 1]替换为arr[++i]将导致错误。为什么?
假设i = 5,那么arr[i + 1]意味着arr[6],而arr[++i]虽然意味着arr[6],但也会将i的值改变为6,这可能不是我们想要做的事情。我们可能不想改变i的值,但由于一个简单的++/——操作符,我们改变了这个值。
所以在使用++/——操作符时要小心。
我希望我能使我的观点更容易理解。
推荐文章
- 每个递归都可以转换成迭代吗?
- 去下一次迭代在java For循环
- 什么是ORM,它是如何工作的,我应该如何使用它?
- 我能在服务器端应用程序(PHP、Ruby、Python等)上读取URL的哈希部分吗?
- 元素在“for(…in…)”循环中排序
- 多少个参数是太多?
- 是否有可能在没有迭代器变量的情况下实现Python for range循环?
- 对于不可变集合上的非突变“add”方法,最好的名称是什么?
- foo到底是什么意思?
- 'for'循环中的后增量和前增量产生相同的输出
- TypeScript for…的索引/键?
- foreach和map有区别吗?
- 打破嵌套循环
- 设计模式:工厂vs工厂方法vs抽象工厂
- 为什么处理排序数组比未排序数组慢?