Compiled this source:
unsafe static void _method1(int val)
{
int* x = &val;
for (int i = 0; i < 10; i++)
{
x = x + 1; // increment x (add 4 to the pointer)
}
}
unsafe static void _method2(int val)
{
int* x = &val;
for (int i = 0; i < 10; i++)
{
x = (int*)(((byte*)x) + 1); // cast to a byte* and increment x (add 1 to the pointer)
}
}
Reflector decompiles it to this (both methods are exactly the same):
private static unsafe void _method1(int val)
{
int* x = &val;
for (int i = 0; i < 10; i++)
{
x++;
}
}
private static unsafe void _method2(int val)
{
int* x = &val;
for (int i = 0; i < 10; i++)
{
x++;
}
}
Here's the disassembly as IL
.method private hidebysig static void _method1(int32 val) cil managed
{
.maxstack 2
.locals init (
[0] int32* x,
[1] int32 i,
[2] bool CS$4$0000)
L_0000: nop
L_0001: ldarga.s val
L_0003: conv.u
L_0004: stloc.0
L_0005: ldc.i4.0
L_0006: stloc.1
L_0007: br.s L_0014
L_0009: nop
L_000a: ldloc.0
>>>
L_000b: ldc.i4.4
>>>
L_000c: conv.i
L_000d: add
L_000e: stloc.0
L_000f: nop
L_0010: ldloc.1
L_0011: ldc.i4.1
L_0012: add
L_0013: stloc.1
L_0014: ldloc.1
L_0015: ldc.i4.s 10
L_0017: clt
L_0019: stloc.2
L_001a: ldloc.2
L_001b: brtrue.s L_0009
L_001d: ret
}
.method private hidebysig static void _method2(int32 val) cil managed
{
.maxstack 2
.locals init (
[0] int32* x,
[1] int32 i,
[2] bool CS$4$0000)
L_0000: nop
L_0001: ldarga.s val
L_0003: conv.u
L_0004: stloc.0
L_0005: ldc.i4.0
L_0006: stloc.1
L_0007: br.s L_0014
L_0009: nop
L_000a: ldloc.0
>>>
L_000b: ldc.i4.1
>>>
L_000c: conv.i
L_000d: add
L_000e: stloc.0
L_000f: nop
L_0010: ldloc.1
L_0011: ldc.i4.1
L_0012: add
L_0013: stloc.1
L_0014: ldloc.1
L_0015: ldc.i4.s 10
L_0017: clt
L_0019: stloc.2
L_001a: ldloc.2
L_001b: brtrue.s L_0009
L_001d: ret
}
The subtle difference is the casting the pointer from (int*) to (byte*) before incrementing in _method2
Reflector decompiles it to this (both methods are exactly the same):
Here's the disassembly as IL
The subtle difference is the casting the pointer from (int*) to (byte*) before incrementing in _method2