如果不使用结果值,i++和++i之间是否有性能差异?
当前回答
简短的回答:
i++和++i在速度上没有任何区别。一个好的编译器不应该在这两种情况下生成不同的代码。
长一点的回答:
其他答案都没有提到的是,++i和i++之间的区别只在它所找到的表达式中有意义。
对于for(i=0;我< n;i++), i++在它自己的表达式中是单独的:在i++之前有一个序列点,在它之后有一个。因此,生成的唯一机器码是“将i增加1”,并且它是如何与程序的其余部分进行排序的。所以如果你把它改成前缀++,这一点关系都没有,你仍然会得到机器代码“将i增加1”。
++i和i++之间的差异只在数组[i++] = x;与数组[++i] = x;有些人可能会争辩说,后缀在这样的操作中会更慢,因为i所在的寄存器稍后必须重新加载。但是请注意,编译器可以自由地以任何它喜欢的方式对你的指令进行排序,只要它不像C标准所说的那样“破坏抽象机器的行为”。
所以当你假设数组[i++] = x;被翻译成机器代码为:
将i的值存储在寄存器A中。 存储寄存器B中数组的地址。 将A和B相加,将结果存储在A中。 在这个由A表示的新地址上,存储x的值。 在寄存器A //中存储i的值是低效的,因为这里有额外的指令,我们已经做过一次了。 增量寄存器A。 在i中存储寄存器A。
编译器也可以更有效地生成代码,例如:
将i的值存储在寄存器A中。 存储寄存器B中数组的地址。 添加A和B,将结果存储在B中。 增量寄存器A。 在i中存储寄存器A。 ... //其余的代码。
只是因为作为一个C程序员,你被训练成认为后缀++发生在结尾,所以机器代码不需要以这种方式排序。
所以在C语言中,前缀++和后缀++没有区别。现在,作为一个C程序员,你应该知道的是,有些人在某些情况下不一致地使用前缀,而在其他情况下不一致地使用后缀,没有任何理由。这表明他们不确定C语言是如何工作的,或者他们对这门语言的了解不正确。这总是一个不好的迹象,它反过来表明他们在他们的程序中做出了其他有问题的决定,基于迷信或“宗教教条”。
“前缀++总是更快”确实是一个错误的教条,在准C程序员中很常见。
其他回答
我可以想到一种情况,后缀比前缀增量慢:
想象一个寄存器a的处理器被用作累加器,它是许多指令中使用的唯一寄存器(一些小型微控制器实际上是这样的)。
现在想象一下下面的程序和它们转换成一个假设的程序集:
前缀增量:
a = ++b + c;
; increment b
LD A, [&b]
INC A
ST A, [&b]
; add with c
ADD A, [&c]
; store in a
ST A, [&a]
后缀增加:
a = b++ + c;
; load b
LD A, [&b]
; add with c
ADD A, [&c]
; store in a
ST A, [&a]
; increment b
LD A, [&b]
INC A
ST A, [&b]
注意b的值是如何被强制重新加载的。使用前缀增量,编译器可以只增加值并继续使用它,可能避免重新加载它,因为所需的值在增量之后已经在寄存器中。然而,使用后缀增量,编译器必须处理两个值,一个是旧值,一个是增加的值,正如我上面所示,这会导致更多的内存访问。
当然,如果增量的值没有被使用,比如单个i++;语句,编译器可以(并且确实)简单地生成一个增量指令,而不管后缀或前缀的使用。
As a side note, I'd like to mention that an expression in which there is a b++ cannot simply be converted to one with ++b without any additional effort (for example by adding a - 1). So comparing the two if they are part of some expression is not really valid. Often, where you use b++ inside an expression you cannot use ++b, so even if ++b were potentially more efficient, it would simply be wrong. Exception is of course if the expression is begging for it (for example a = b++ + 1; which can be changed to a = ++b;).
请不要让“哪个更快”的问题成为使用哪个的决定因素。你可能永远不会关心那么多,此外,程序员的阅读时间比机器的时间要昂贵得多。
使用任何对阅读代码的人最有意义的方法。
摘自Andrew Koenig的《效率与意图》:
首先,++i是否比i++更有效还不明显,至少在涉及整型变量时是这样。
和:
所以人们应该问的问题不是这两种操作中哪一种更快,而是这两种操作中哪一种更准确地表达了你想要完成的事情。我认为,如果你不使用表达式的值,永远没有理由使用i++而不是++ I,因为永远没有理由复制一个变量的值,增加变量,然后扔掉拷贝。
因此,如果没有使用结果值,则使用++ I。但不是因为它更有效,而是因为它正确地表达了我的意图。
如果你担心微观优化,这里有一个额外的观察。递减循环“可能”比递增循环更有效(取决于指令集架构,例如ARM),给定:
for (i = 0; i < 100; i++)
在每个循环中,你将有一个指令:
i加1。 比较i是否小于100。 如果i小于100,则为条件分支。
而递减循环:
for (i = 100; i != 0; i--)
循环将有一个指令用于以下每一个:
递减i,设置CPU寄存器状态标志。 一个依赖于CPU寄存器状态(Z==0)的条件分支。
当然,这只适用于递减到零!
记得ARM系统开发人员指南。
简短的回答:
i++和++i在速度上没有任何区别。一个好的编译器不应该在这两种情况下生成不同的代码。
长一点的回答:
其他答案都没有提到的是,++i和i++之间的区别只在它所找到的表达式中有意义。
对于for(i=0;我< n;i++), i++在它自己的表达式中是单独的:在i++之前有一个序列点,在它之后有一个。因此,生成的唯一机器码是“将i增加1”,并且它是如何与程序的其余部分进行排序的。所以如果你把它改成前缀++,这一点关系都没有,你仍然会得到机器代码“将i增加1”。
++i和i++之间的差异只在数组[i++] = x;与数组[++i] = x;有些人可能会争辩说,后缀在这样的操作中会更慢,因为i所在的寄存器稍后必须重新加载。但是请注意,编译器可以自由地以任何它喜欢的方式对你的指令进行排序,只要它不像C标准所说的那样“破坏抽象机器的行为”。
所以当你假设数组[i++] = x;被翻译成机器代码为:
将i的值存储在寄存器A中。 存储寄存器B中数组的地址。 将A和B相加,将结果存储在A中。 在这个由A表示的新地址上,存储x的值。 在寄存器A //中存储i的值是低效的,因为这里有额外的指令,我们已经做过一次了。 增量寄存器A。 在i中存储寄存器A。
编译器也可以更有效地生成代码,例如:
将i的值存储在寄存器A中。 存储寄存器B中数组的地址。 添加A和B,将结果存储在B中。 增量寄存器A。 在i中存储寄存器A。 ... //其余的代码。
只是因为作为一个C程序员,你被训练成认为后缀++发生在结尾,所以机器代码不需要以这种方式排序。
所以在C语言中,前缀++和后缀++没有区别。现在,作为一个C程序员,你应该知道的是,有些人在某些情况下不一致地使用前缀,而在其他情况下不一致地使用后缀,没有任何理由。这表明他们不确定C语言是如何工作的,或者他们对这门语言的了解不正确。这总是一个不好的迹象,它反过来表明他们在他们的程序中做出了其他有问题的决定,基于迷信或“宗教教条”。
“前缀++总是更快”确实是一个错误的教条,在准C程序员中很常见。