views:

306

answers:

3

I have a small question about structures with the LayoutKind.Explicit attribute set. I declared the struct as you can see, with a fieldTotal with 64 bits, being fieldFirst the first 32 bytes and fieldSecond the last 32 bytes. After setting both fieldfirst and fieldSecond to Int32.MaxValue, I'd expect fieldTotal to be Int64.MaxValue, which actually doesn't happen. Why is this? I know C# does not really support C++ unions, maybe it will only read the values well when interoping, but when we try to set the values ourselves it simply won't handle it really well?

[StructLayout(LayoutKind.Explicit)]
struct STRUCT {
    [FieldOffset(0)]
    public Int64 fieldTotal;

    [FieldOffset(0)]
    public Int32 fieldFirst;

    [FieldOffset(32)]
    public Int32 fieldSecond;
}

        STRUCT str = new STRUCT();
        str.fieldFirst = Int32.MaxValue;
        str.fieldSecond = Int32.MaxValue;
        Console.WriteLine(str.fieldTotal); // <----- I'd expect both these values
        Console.WriteLine(Int64.MaxValue); // <----- to be the same.
        Console.ReadKey();
+5  A: 

Looking at the hex values if Int32.MaxValue and Int64.MaxValue should provide the answer.

The key is the most significant bit. For a positive integer, the most significant bit is only set for a negative number. So the max value of Int32 is a 0 followed by a whole series of 1s. The order is unimportant, just that there will be at least a single 0 bit. The same is true of Int64.MaxValue.

Now consider how a union should work. It will essentially lay out the bits of the values next to one another. So now you have a set of bits 64 in length which contains two 0 bit values. One for each of the Int32.MaxValue instances. This cannot ever be equal to Int64.MaxValue since it can contain only a single 0 bit.

Oddly enough you will probably get the behavior you are looking for if you set fieldSecond to Int32.MinValue.

EDIT Missed that you need to make it FieldOffset(4) as well.

JaredPar
+10  A: 

The reason is that FieldOffsetAttribute takes a number of bytes as parameter -- not number of bits. This works as expected:

[StructLayout(LayoutKind.Explicit)]
struct STRUCT
{
    [FieldOffset(0)]
    public Int64 fieldTotal;

    [FieldOffset(0)]
    public Int32 fieldFirst;

    [FieldOffset(4)]
    public Int32 fieldSecond;
}
Ben M
Ben M
+4  A: 

Ben M provided one of the more important elements - your definition is not setup correctly.

That being said, this won't work - even in C++ with a union. The values you specified won't be (and shouldn't be) the same values, since you're using signed (not unsigned) ints. With a signed int (Int32), you're going to have a 0 bit followed by 1 bits. When you do the union, you'll end up with a 0 bit, followed by a bunch of 1 bits, then another 0 bit, then a bunch of 1 bits... The second 0 bit is what's messing you up.

If you used UInt32/UInt64, this would work property, since the extra sign bit doesn't exist.

Reed Copsey
Yes, you are right. With what you said and Ben said, it works as expected. Thanks!
devoured elysium