如果不使用结果值,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程序员中很常见。