如果不使用结果值,i++和++i之间是否有性能差异?


当前回答

更好的答案是++i有时会更快,但绝不会变慢。

每个人似乎都认为i是一个常规的内置类型,比如int。在这种情况下,将没有可测量的差异。

然而,如果i是复型,那么你很可能会发现一个可测量的差异。对于i++,您必须在递增类之前复制它。根据复制中涉及的内容,它确实可能会变慢,因为使用++i可以只返回最终值。

Foo Foo::operator++()
{
  Foo oldFoo = *this; // copy existing value - could be slow
  // yadda yadda, do increment
  return oldFoo;
}

另一个区别是,使用++i,您可以选择返回一个引用而不是一个值。同样,根据复制对象所涉及的内容,这可能会更慢。

在现实世界中,迭代器的使用就是可能发生这种情况的一个例子。复制迭代器不太可能成为应用程序中的瓶颈,但养成使用++i而不是i++的习惯仍然是一个很好的实践,因为i++的结果不会受到影响。

其他回答

执行摘要:没有。

i++可能比++ I慢,因为I的旧值 可能需要保存以备以后使用,但实际上都是现代的 编译器将对其进行优化。

我们可以通过查看这个函数的代码来证明这一点, 都是++i和i++。

$ cat i++.c
extern void g(int i);
void f()
{
    int i;

    for (i = 0; i < 100; i++)
        g(i);

}

文件是一样的,除了++i和i++:

$ diff i++.c ++i.c
6c6
<     for (i = 0; i < 100; i++)
---
>     for (i = 0; i < 100; ++i)

我们将编译它们,并获得生成的汇编器:

$ gcc -c i++.c ++i.c
$ gcc -S i++.c ++i.c

我们可以看到生成的对象和汇编程序文件都是相同的。

$ md5 i++.s ++i.s
MD5 (i++.s) = 90f620dda862cd0205cd5db1f2c8c06e
MD5 (++i.s) = 90f620dda862cd0205cd5db1f2c8c06e

$ md5 *.o
MD5 (++i.o) = dd3ef1408d3a9e4287facccec53f7d22
MD5 (i++.o) = dd3ef1408d3a9e4287facccec53f7d22

我可以想到一种情况,后缀比前缀增量慢:

想象一个寄存器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;).

我的C有点生疏了,所以我提前道歉。就速度而言,我可以理解结果。但是,我对这两个文件是如何得到相同的MD5哈希感到困惑。也许for循环也可以运行,但是下面两行代码不会生成不同的程序集吗?

myArray[i++] = "hello";

vs

myArray[++i] = "hello";

第一个函数将值写入数组,然后将i加1,第二个函数将i加1,然后将值写入数组。我不是汇编专家,但我只是不明白这两行不同的代码如何生成相同的可执行文件。

这只是我的个人意见。

在C语言中,如果结果未被使用,编译器通常可以将它们优化为相同。

然而,在c++中,如果使用提供自己的++操作符的其他类型,前缀版本可能比后缀版本更快。因此,如果不需要后缀语义,最好使用前缀操作符。

@Mark 即使编译器允许优化(基于堆栈的)变量的临时副本,并且gcc(在最近的版本中)正在这样做, 这并不意味着所有编译器都会这样做。

我刚刚用我们在当前项目中使用的编译器测试了它,4个中有3个没有优化它。

永远不要假设编译器是正确的,特别是如果可能更快,但永远不会更慢的代码很容易阅读。

如果你的代码中没有一个操作符的愚蠢实现:

我喜欢++i胜过i++。