Post Increment And Assign Integer – C# experiments

Back to experiments • This technical report was last updated on May 13, 2012 by Paul V. Borza
Paul V. Borza

Paul is a software engineer born in Transylvania who lives in the United States of America and works for Microsoft. About

The opinions and views expressed on this site are those of the author and do not necessarily state or reflect those of Microsoft.

Problem

i = 0; i = i++; Console.Write(i);
What's going to be the output written to the console, 0 or 1?

It's 0 (zero).
Although this is interesting, you should never use such a construct in your code as it's confusing.

Test1
OutcomeAverage (sec.)
Variant1Passed0.000·032·699
Variant2Passed-9.05%0.000·029·741

Interface

namespace PostIncrementAndAssignInteger
{
    public interface IPostIncrementAndAssignInteger
    {
        int PostIncrementAndAssign();
    }
}
.class interface public abstract auto ansi PostIncrementAndAssignInteger.IPostIncrementAndAssignInteger
{
  .method public hidebysig newslot abstract virtual 
          instance int32  PostIncrementAndAssign() cil managed
  {
  } // end of method IPostIncrementAndAssignInteger::PostIncrementAndAssign

} // end of class PostIncrementAndAssignInteger.IPostIncrementAndAssignInteger

Tests

namespace PostIncrementAndAssignInteger.Tests
{
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using PostIncrementAndAssignInteger;

    public class Tests
    {
        public void Test1<T>() where T : IPostIncrementAndAssignInteger, new()
        {
            IPostIncrementAndAssignInteger variant = new T();

            Assert.AreEqual(0, variant.PostIncrementAndAssign());
        }
    }
}

Top

Variants

Variant1

BestWorstAverage
O(1)O(1)O(1)Time
O(1)O(1)O(1)Space

The result is 0 for Microsoft's Common Language Runtime.
Take a look at the IL code:
For the first line "var i = 0;", this is what's going on:
1. ldc.i4.0: pushes zero as a four byte integer on the stack
2. stloc.0: pops the value zero from the stack into i
For the second line "i = i++;", this is what's going on:
1. ldloc.0: loads i on the stack
2. dup: duplicates the value on the stack (so zero is duplicated)
3. ldc.i4.1: pushes one as a four byte integer on the stack
4. add.ovf: adds signed integer values from the top of the stack (zero and one) with overflow checking (overflow checking is irrelevant though in this case)
5. stloc.0: pops the value one from the top of the stack into i (so 1 == i at this point)
6. stloc.0: pops the other value, which was zero, from the top of the stack into i (so 0 == i at this point, since the second top most value from the stack was not changed)
So there you go, it's zero.

References

namespace PostIncrementAndAssignInteger
{
    using System.Diagnostics.CodeAnalysis;

    [SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1512:SingleLineCommentsMustNotBeFollowedByBlankLine", Justification = "Alternatives")]
    public sealed class Variant1 : IPostIncrementAndAssignInteger
    {
        public int PostIncrementAndAssign()
        {
            var i = 0;
            i = i++;

            // the above code is equivalent to the below code
            // int old = i;
            // i = i + 1;
            // i = old;

            return i; // 0
        }
    }
}
.class public auto ansi sealed beforefieldinit PostIncrementAndAssignInteger.Variant1
       extends [mscorlib]System.Object
       implements PostIncrementAndAssignInteger.IPostIncrementAndAssignInteger
{
  .custom instance void [mscorlib]System.Diagnostics.CodeAnalysis.SuppressMessageAttribute::.ctor(string,
                                                                                                  string) = ( 01 00 1B 53 74 79 6C 65 43 6F 70 2E 43 53 68 61   // ...StyleCop.CSha
                                                                                                              72 70 2E 4C 61 79 6F 75 74 52 75 6C 65 73 35 53   // rp.LayoutRules5S
                                                                                                              41 31 35 31 32 3A 53 69 6E 67 6C 65 4C 69 6E 65   // A1512:SingleLine
                                                                                                              43 6F 6D 6D 65 6E 74 73 4D 75 73 74 4E 6F 74 42   // CommentsMustNotB
                                                                                                              65 46 6F 6C 6C 6F 77 65 64 42 79 42 6C 61 6E 6B   // eFollowedByBlank
                                                                                                              4C 69 6E 65 01 00 54 0E 0D 4A 75 73 74 69 66 69   // Line..T..Justifi
                                                                                                              63 61 74 69 6F 6E 0C 41 6C 74 65 72 6E 61 74 69   // cation.Alternati
                                                                                                              76 65 73 )                                        // ves
  .method public hidebysig newslot virtual final 
          instance int32  PostIncrementAndAssign() cil managed
  {
    // Code size       15 (0xf)
    .maxstack  3
    .locals init ([0] int32 i,
             [1] int32 CS$1$0000)
//000040:         {
    IL_0000:  nop
//000041:             var i = 0;
    IL_0001:  ldc.i4.0
    IL_0002:  stloc.0
//000042:             i = i++;
    IL_0003:  ldloc.0
    IL_0004:  dup
    IL_0005:  ldc.i4.1
    IL_0006:  add.ovf
    IL_0007:  stloc.0
    IL_0008:  stloc.0
//000043: 
//000044:             // the above code is equivalent to the below code
//000045:             // int old = i;
//000046:             // i = i + 1;
//000047:             // i = old;
//000048: 
//000049:             return i; // 0
    IL_0009:  ldloc.0
    IL_000a:  stloc.1
    IL_000b:  br.s       IL_000d

//000050:         }
    IL_000d:  ldloc.1
    IL_000e:  ret
  } // end of method Variant1::PostIncrementAndAssign

  .method public hidebysig specialname rtspecialname 
          instance void  .ctor() cil managed
  {
    // Code size       7 (0x7)
    .maxstack  8
    IL_0000:  ldarg.0
    IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
    IL_0006:  ret
  } // end of method Variant1::.ctor

} // end of class PostIncrementAndAssignInteger.Variant1
LinesBlocks
CoveredPartially coveredNot coveredCoveredNot covered
50020
Coverage of Variant1 linesCoverage of Variant1 blocks

Top

Variant2

BestWorstAverage
O(1)O(1)O(1)Time
O(1)O(1)O(1)Space

You might think that the volatile keyword will forbid the kind of behavior exposed in the first variant, but it doesn't.
The volatile modifier is usually used for a field that is accessed by multiple threads without using the lock statement to serialize access.

Fields that are declared volatile are not subject to compiler optimizations that assume access by a single thread.
This ensures that the most up-to-date value is present in the field at all times.

References

namespace PostIncrementAndAssignInteger
{
    using System.Diagnostics.CodeAnalysis;

    public sealed class Variant2 : IPostIncrementAndAssignInteger
    {
        private volatile int i = 0;

        public int PostIncrementAndAssign()
        {
            this.i = this.i++;

            return this.i; // 0
        }
    }
}
.class public auto ansi sealed beforefieldinit PostIncrementAndAssignInteger.Variant2
       extends [mscorlib]System.Object
       implements PostIncrementAndAssignInteger.IPostIncrementAndAssignInteger
{
  .field private int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) i
  .method public hidebysig newslot virtual final 
          instance int32  PostIncrementAndAssign() cil managed
  {
    // Code size       43 (0x2b)
    .maxstack  4
    .locals init ([0] int32 CS$1$0000,
             [1] int32 CS$0$0001)
    .language '{3F5162F8-07C6-11D3-9053-00C04FA302A1}', '{994B45C4-E6E9-11D2-903F-00C04FA302A1}', '{5A869D0B-6611-11D3-BD2A-0000F80849BD}'
//000031:         {
    IL_0000:  nop
//000032:             this.i = this.i++;
    IL_0001:  ldarg.0
    IL_0002:  ldarg.0
    IL_0003:  dup
    IL_0004:  volatile.
    IL_0006:  ldfld      int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) PostIncrementAndAssignInteger.Variant2::i
    IL_000b:  dup
    IL_000c:  stloc.1
    IL_000d:  ldc.i4.1
    IL_000e:  add.ovf
    IL_000f:  volatile.
    IL_0011:  stfld      int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) PostIncrementAndAssignInteger.Variant2::i
    IL_0016:  ldloc.1
    IL_0017:  volatile.
    IL_0019:  stfld      int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) PostIncrementAndAssignInteger.Variant2::i
//000033: 
//000034:             return this.i; // 0
    IL_001e:  ldarg.0
    IL_001f:  volatile.
    IL_0021:  ldfld      int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) PostIncrementAndAssignInteger.Variant2::i
    IL_0026:  stloc.0
    IL_0027:  br.s       IL_0029

//000035:         }
    IL_0029:  ldloc.0
    IL_002a:  ret
  } // end of method Variant2::PostIncrementAndAssign

  .method public hidebysig specialname rtspecialname 
          instance void  .ctor() cil managed
  {
    // Code size       17 (0x11)
    .maxstack  8
//000028:         private volatile int i = 0;
    IL_0000:  ldarg.0
    IL_0001:  ldc.i4.0
    IL_0002:  volatile.
    IL_0004:  stfld      int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) PostIncrementAndAssignInteger.Variant2::i
    IL_0009:  ldarg.0
    IL_000a:  call       instance void [mscorlib]System.Object::.ctor()
    IL_000f:  nop
    IL_0010:  ret
  } // end of method Variant2::.ctor

} // end of class PostIncrementAndAssignInteger.Variant2
LinesBlocks
CoveredPartially coveredNot coveredCoveredNot covered
50030
Coverage of Variant2 linesCoverage of Variant2 blocks

Top

Comments