tags:

views:

116

answers:

3

What goes on at a low level when I do this?

Int32 a = 0;
Int16 b = 50;

a = b;
+5  A: 

Something like this:

IL_0001:  /* 1F   | 32               */ ldc.i4.s   50
IL_0003:  /* 0B   |                  */ stloc.1
IL_0004:  /* 07   |                  */ ldloc.1
IL_0005:  /* 0A   |                  */ stloc.0

At a lower level, it depends on the machine architecture and optimization level. Code like this specifically, that has no effect, will probably just be omitted altogether. Otherwise, it'll be simple code, perhaps like this:

movsx eax, word ptr [ebp+12]
mov [ebp+8], eax

movsx is the x86 instruction which preserves the sign of a shorter number when it's being loaded into a larger destination; basically, it looks at the most significant bit of the smaller source and copies it into the remaining bits when it's extending the number.

Barry Kelly
HeHe... That's really low level... I was thinking along the lines of a narrowing conversion takes place... blah...blah... blah.... :)
klabranche
Very interesting, I've worked with Motorola assembly but it has been a while. Thanks.
ChaosPandion
There's no narrowing conversion needed in the code you present; the 50 is statically known to fit in range, and the assignment widens, not narrows. For narrowing explicitly, `conv.[type]` IL can be used, e.g. `conv.i2` to truncate top of stack to Int16. In x86 assembly, depending on how it's used, no conversion may be needed, or an `and 0xffff` may be applied, etc.
Barry Kelly
+2  A: 

From Reflector:

.method public hidebysig static void Main() cil managed
{
    .entrypoint
    .maxstack 1
    .locals init (
        [0] int32 num,
        [1] int16 num2)
    L_0000: nop 
    L_0001: ldc.i4.0          ; Load the constant 0
    L_0002: stloc.0           ; Store the value into local var 0
    L_0003: ldc.i4.s 50       ; Load the constant 50 - notice it treats it as a 32-bit value
    L_0005: stloc.1           ; Store the value into local var 1
    L_0006: ldloc.1           ; Load local var 1
    L_0007: stloc.0           ; Store the value into local var 0
    L_0008: ret 
}

At the IL level, nothing special happens in the assignment, but notice that ldc.i4.s 50 treats the literal value as a 4-byte (32-bit) integer.

When the code is JIT compiled, the resulting assembly code probably just promotes the value 50 to a 32-bit wide value.

Brannon
A: 

The evaluation stack doesn't have a representation smaller than 32-bits. [Edit, in most cases (thanks for the update from the comment :)] Until you actually store a 16-bit value somewhere other than the stack, nothing special happens because the short and int are the same size. Here are the only operations where you would see a difference:

  • b = (short)a
    Will sign-extend bit 16 of the result to a full 32-bit wide variable. This is the conv.i4 instruction mentioned in the comment.
  • *(short*)c = b or assignment to a struct member where the struct is marked StructLayout.Explicit or has a packing less than 4.
    Will only write the lower 16 bits of its 32-bit representation.
  • checked { b = (short)a; }
    Will throw an exception if (a < -32768 || a > 32767).
280Z28
The bit about no effect until storing other than stack isn't quite true; typecasting e.g. an Int32 to an Int16 will lead to truncation of the value on the stack, through e.g. conv.i2.
Barry Kelly
@Barry: Thanks. I caught it in my list but I forgot to update the intro paragraph before posting. :)
280Z28