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
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