我们的问题是,在C语言中i++和++i的性能有区别吗?

c++的答案是什么?


当前回答

是的。有。

++操作符可以定义为函数,也可以不定义为函数。对于基本类型(int, double,…),操作符是内置的,因此编译器可能能够优化您的代码。但对于定义了++运算符的对象,情况就不一样了。

操作符++(int)函数必须创建一个副本。这是因为postfix ++被期望返回一个与它所保存的值不同的值:它必须将其值保存在一个临时变量中,自增其值并返回临时值。在操作符++(),前缀++的情况下,不需要创建一个副本:对象可以自增,然后简单地返回自己。

下面是关于这一点的一个例子:

struct C
{
    C& operator++();      // prefix
    C  operator++(int);   // postfix

private:

    int i_;
};

C& C::operator++()
{
    ++i_;
    return *this;   // self, no copy created
}

C C::operator++(int ignored_dummy_value)
{
    C t(*this);
    ++(*this);
    return t;   // return a copy
}

每次调用操作符++(int)都必须创建一个副本,编译器对此无能为力。当有选择时,使用运算符++();这样就不需要保存副本。在很多增量(大循环?)和/或大对象的情况下,它可能很重要。

其他回答

即使在没有性能优势的内置类型上也应该使用++i的原因是为了给自己养成一个好习惯。

谷歌c++风格指南说:

Preincrement and Predecrement Use prefix form (++i) of the increment and decrement operators with iterators and other template objects. Definition: When a variable is incremented (++i or i++) or decremented (--i or i--) and the value of the expression is not used, one must decide whether to preincrement (decrement) or postincrement (decrement). Pros: When the return value is ignored, the "pre" form (++i) is never less efficient than the "post" form (i++), and is often more efficient. This is because post-increment (or decrement) requires a copy of i to be made, which is the value of the expression. If i is an iterator or other non-scalar type, copying i could be expensive. Since the two types of increment behave the same when the value is ignored, why not just always pre-increment? Cons: The tradition developed, in C, of using post-increment when the expression value is not used, especially in for loops. Some find post-increment easier to read, since the "subject" (i) precedes the "verb" (++), just like in English. Decision: For simple scalar (non-object) values there is no reason to prefer one form and we allow either. For iterators and other template types, use pre-increment.

++i -更快,不使用返回值 i++ -使用返回值更快

当不使用返回值时,编译器保证不会在++i的情况下使用临时类型。不保证更快,但保证不会变慢。

当使用返回值i++时,允许处理器同时推送 增量和左侧进入管道,因为它们彼此不依赖。i可能会使管道停止,因为处理器无法启动左侧,直到增量前操作已经蜿蜒完成。同样,也不保证会出现管道失速,因为处理器可能会找到其他有用的东西来插入。

说编译器不能优化掉后缀情况下的临时变量副本是不完全正确的。用VC进行的快速测试表明,至少在某些情况下,它可以做到这一点。

在下面的例子中,生成的代码对于前缀和后缀是相同的,例如:

#include <stdio.h>

class Foo
{
public:

    Foo() { myData=0; }
    Foo(const Foo &rhs) { myData=rhs.myData; }

    const Foo& operator++()
    {
        this->myData++;
        return *this;
    }

    const Foo operator++(int)
    {
        Foo tmp(*this);
        this->myData++;
        return tmp;
    }

    int GetData() { return myData; }

private:

    int myData;
};

int main(int argc, char* argv[])
{
    Foo testFoo;

    int count;
    printf("Enter loop count: ");
    scanf("%d", &count);

    for(int i=0; i<count; i++)
    {
        testFoo++;
    }

    printf("Value: %d\n", testFoo.GetData());
}

无论您使用的是++testFoo还是testfoo++,都将得到相同的结果代码。事实上,无需从用户读取计数,优化器将整个事情归结为一个常数。所以这个:

for(int i=0; i<10; i++)
{
    testFoo++;
}

printf("Value: %d\n", testFoo.GetData());

结果如下:

00401000  push        0Ah  
00401002  push        offset string "Value: %d\n" (402104h) 
00401007  call        dword ptr [__imp__printf (4020A0h)] 

因此,虽然后缀版本肯定会更慢,但如果你不使用它,优化器可能会足够好,可以摆脱临时副本。

是的。有。

++操作符可以定义为函数,也可以不定义为函数。对于基本类型(int, double,…),操作符是内置的,因此编译器可能能够优化您的代码。但对于定义了++运算符的对象,情况就不一样了。

操作符++(int)函数必须创建一个副本。这是因为postfix ++被期望返回一个与它所保存的值不同的值:它必须将其值保存在一个临时变量中,自增其值并返回临时值。在操作符++(),前缀++的情况下,不需要创建一个副本:对象可以自增,然后简单地返回自己。

下面是关于这一点的一个例子:

struct C
{
    C& operator++();      // prefix
    C  operator++(int);   // postfix

private:

    int i_;
};

C& C::operator++()
{
    ++i_;
    return *this;   // self, no copy created
}

C C::operator++(int ignored_dummy_value)
{
    C t(*this);
    ++(*this);
    return t;   // return a copy
}

每次调用操作符++(int)都必须创建一个副本,编译器对此无能为力。当有选择时,使用运算符++();这样就不需要保存副本。在很多增量(大循环?)和/或大对象的情况下,它可能很重要。