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+++")在增量完成前返回变量的结果。编译器可以(并且非常强调可以)更容易地使用预增量进行优化(因为只有一个值被有效地使用;在后增量的情况下,有两个值:增量前的变量值和增量后的变量值;编译器可以优化预增量以使用存储它包含变量本身)。
请注意,这是非常理论化的;它很大程度上取决于您的编译器优化。在大多数情况下,而且在大多数情况下,它们实际上是等效有效的。
这是一个很好的增量前后的总结。
综上所述,预增量
我认为更重要的是了解它们的功能差异,而不是担心它们的速度。对写作业使用写作业。
这不是优化问题,而是功能问题。
在这里解释…在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++; |
所以,如果有疑问,保持简单,如果你是在一个紧环,需要额外的一点,就把它分开。
编辑:将示例代码更改为我观察到的一些实际代码。