如果不使用结果值,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和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

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

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

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

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

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

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

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

参考Scott Meyers的《更有效的c++第六条:区分增减操作的前缀和后缀形式》。

对于对象,尤其是对于迭代器,前缀版本总是优于后缀版本。

原因是,如果你看一下操作符的调用模式。

// Prefix
Integer& Integer::operator++()
{
    *this += 1;
    return *this;
}

// Postfix
const Integer Integer::operator++(int)
{
    Integer oldValue = *this;
    ++(*this);
    return oldValue;
}

看看这个例子,很容易看出前缀操作符总是比后缀操作符更有效率。因为需要在临时对象中使用后缀。

这就是为什么当你看到使用迭代器的例子时,他们总是使用前缀版本。

但正如你所指出的,对于int型,实际上没有什么区别,因为编译器优化可以发生。

我总是喜欢预增量,然而……

我想指出的是,即使在调用运算符++函数的情况下,如果函数得到内联,编译器将能够优化掉临时函数。由于操作符++通常很短,并且经常在头文件中实现,因此它很可能被内联。

因此,出于实际目的,这两种形式的性能之间可能没有太大差异。然而,我总是喜欢预增量,因为它似乎更好地直接表达我想说的,而不是依赖于优化器来解决它。

此外,给优化器更少的任务可能意味着编译器运行得更快。