在C#中,如果是正确的单词:++X或X++,哪个更优化?


In C#, which is more optimized, if that is the right word: ++x or x++?

本问题已经有最佳答案,请猛点这里访问。

完全复制:

In C# what is the difference between myint++ and ++myint?

希望这不是一个太笼统或奇怪的问题。另外,在谷歌上找不到它,不要说这是一个问题太愚蠢还是我在谷歌上失败了。

所以,我忘记了我从哪里读到的,但是,它说使用"++X"(或者其他变量)比"X++"更优化,或者你可能称之为它的任何东西。

所以,这只是一个看起来的东西还是一个真正更快的?就像,他们做同样的事情,所以,这就是我问的原因。


他们在做不同的事情。第一个是预增量,第二个是后增量。如果你在一条线上单独使用,你不会注意到区别。但如果你这样做(例如):

1
a = x++

1
a = ++x

你会。使用post increment,可以得到值,然后是increment。使用pre-increment,您将递增,然后获取值。请参见http://en.wikipedia.org/wiki/++在诳编程诳语言中使用诳作简要解释(他们以JS为例,但它同样适用于C诳)

编辑:根据流行的需求(并驳斥一对夫妇的夸张),这里有一些关于C++中的原语的性能。以这个示例程序为例:

1
2
3
4
5
6
7
int main()
{
    int a;
    int x = 14, y = 19;
    a = x++;    
    a = ++y;    
}

它在x86上将(g++,just-s)编译为以下内容。我删掉了不相关的台词。让我们看看发生了什么,看看是否有不必要的重复。

1
2
3
4
5
6
7
8
9
10
11
        # Obvious initialization.
        movl    $14, -12(%ebp)
        movl    $19, -16(%ebp)

        movl    -12(%ebp), %eax # This is moving"old x" to the accumulator.
        movl    %eax, -8(%ebp) # Moving accumulator to a.
        addl    $1, -12(%ebp) # Increment x (post-increment).

        addl    $1, -16(%ebp) # Increment y (pre-increment)
        movl    -16(%ebp), %eax # Move"new y" to accumulator.
        movl    %eax, -8(%ebp) # Move accumulator to a.

我们完了。

如您所见,在本例中,每种情况下都需要完全相同的操作。正好2个movl和1个addl。唯一的区别是顺序(惊讶吗?).I认为这是使用increment语句值的典型示例。


如果你在这个水平上优化,你就是在浪费时间。你的程序中肯定有一些循环会更快。


好的,预增量操作("++X")在增量完成后返回变量的结果,而后增量操作("X+++")在增量完成前返回变量的结果。编译器可以(并且非常强调可以)更容易地使用预增量进行优化(因为只有一个值被有效地使用;在后增量的情况下,有两个值:增量前的变量值和增量后的变量值;编译器可以优化预增量以使用存储它包含变量本身)。

请注意,这是非常理论化的;它很大程度上取决于您的编译器优化。在大多数情况下,而且在大多数情况下,它们实际上是等效有效的。


这是一个很好的增量前后的总结。

综上所述,预增量++x比较简单,应该优先考虑。然而,在实践中,我怀疑它是否有显著的区别。

我认为更重要的是了解它们的功能差异,而不是担心它们的速度。对写作业使用写作业。


x++将增加x,但返回其旧值。++x增加x并返回新值。区别在于x++需要声明一个隐藏的temp变量来存储旧值。


这不是优化问题,而是功能问题。

在这里解释…在C中,myint++和+myint有什么区别?


在C(因为问题标签中提到了这一点),如果单独使用,这两者不应该太不同。

在C++中,EDOCX1×0是优于EDCOX1(1)的,因为(对于具有重载运算符的类),最佳实践是以增量增量来实现后增量。

1
2
3
4
5
6
7
8
9
10
11
// pre-increment (note, return by reference)
T& operator++() {
    // ... do its thing
}

// post-increment (note, return by value)
T operator++(int) {
    T saved(*this);
    ++*this;  // or: operator++();
    return saved;
}


我觉得这是个有趣的问题。我不知道在从变量的右侧或左侧使用增量运算符时,性能会有所不同。显然有C++(谢谢Chris Jester Young,1)。

我决定自己看看clr对这个操作符做了什么。我创建了两个函数:

1
2
3
4
5
6
7
8
9
private int IncrementRight(int n)
{
    return n++;
}

private int IncrementLeft(int n)
{
    return ++n;
}

我查看了Reflector中编译的代码。IL看起来几乎是相同的;区别只在于操作码的顺序。

这是两种方法的IL:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
.method private hidebysig instance int32 IncrementLeft(int32 n) cil managed
{
    .maxstack 8
    L_0000: ldarg.1
    L_0001: ldc.i4.1
    L_0002: add
    L_0003: dup
    L_0004: starg.s n
    L_0006: ret
}


.method private hidebysig instance int32 IncrementRight(int32 n) cil managed
{
    .maxstack 8
    L_0000: ldarg.1
    L_0001: dup
    L_0002: ldc.i4.1
    L_0003: add
    L_0004: starg.s n
    L_0006: ret
}

因此,我的结论是,当使用递增运算符时,C没有区别。


前/后增量(FogCreek)

preincrement比post increment更有效,因为对于post increment,对象必须自己递增,然后返回包含其旧值的临时值。请注意,即使对于像int这样的内置函数,也是如此!

暂时的事情才是造成差异的原因。

对象越大,内存使用越差。

如果我错了请纠正我。


编译器本身使用(如果不使用返回值),它应该足够聪明,能够为两者生成相同的代码。

而且,您应该尝试自己使用它,除非您特别需要某个东西的后增量或前增量行为。我发现这样的代码:

1
r[ofs] = *(p++);

略慢于:

1
2
r[ofs] = *p;
p++;

所以,如果有疑问,保持简单,如果你是在一个紧环,需要额外的一点,就把它分开。

编辑:将示例代码更改为我观察到的一些实际代码。