8

I am used a structure to represent pure data. One of the fields is a fixed-size buffer, as shown below.

[StructLayout(LayoutKind.Sequential, Pack=2)]
unsafe struct ImageDosHeader
{
    ...
    private fixed ushort _e_res[4];
    ...

    [Description("Reserved")]
    [DisplayName("e_res[0]")]
    public ushort e_res_0 { get { ... } set { ... } }

    ...
}

Within the get/set functions I tried to do the following but I get "Compiler Error CS1666: You cannot use fixed size buffers contained in unfixed expressions. Try using the fixed statement."

return this._e_res[0];

However, the following work:

fixed (ImageDosHeader* p = &this)
    return p->_e_res[0];

ImageDosHeader local = this;
return local._e_res[0];

I can easily use the workarounds, however, I am wondering why directly accessing the fixed-size buffer from this is illegal. Or is this a bug that I should report?

I am using .NET 2.0.

2 Answers 2

12

It's because of the underlying IL instructions.

The program does this sequence of instructions to get the element you want:

  1. Load the address onto the stack.

  2. Load the offset onto the stack.

  3. Add them.

  4. Read the value at that memory address.

If the object is in the heap and then moves because of garbage collection before step 4, then the address loaded from step 1 will no longer be valid. To protect against this, you need to pin the object into memory first.

(The fact that you're accessing the structure through the this pointer means that you have no idea if the structure is on the heap or on the stack, so you have to pin it just in case it's on the heap.)

The second example works because it copies the structure to the stack, and so the copy can never move around, so the address will always be valid.

Why doesn't the same issue happen with other kinds of fields? Because their offset is known at compile-time, whereas the array index is known at run-time, so the JIT can generate code that will always access the fields correctly.

1
  • 1
    Thanks! Due to your comments about method #2 (copying to a local variable) I realized that that method would not work at all for the "set" method. Commented May 16, 2011 at 0:37
6

The perspective from which you look at the fixed keyword changes its semantics, which is rather confusing. The fixed's statement original purpose has been to pin a piece of blittable memory in place, in C# 2.0 it is used along with a field declaration to denote that 'the array is exactly N elements long', thus, of fixed size, not fixed in memory.

I'd get rid of the fixed keyword in field declaration and just use:

[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] private ushort[] _e_res;

This way, the struct is still blittable and not a pain in the butt to work with.

13
  • Just don't try taking the address of the structure. ;)
    – user541686
    Commented May 16, 2011 at 1:55
  • The trick is to not take the address of the structure in the first place, since it is not needed. This code allows OP to call: return this._e_res[0]; while retaining blittable semantics. If you explicitly need the address, just use what GCHandle has been made for.
    – arul
    Commented May 16, 2011 at 2:15
  • @arul: Yeah, you don't need the address if you're working with a single structure. If you're working with an array of structures, though, you might want to think twice before using .NET arrays. :) (Also, GCHandle isn't a great idea here -- it performs boxing.)
    – user541686
    Commented May 16, 2011 at 2:18
  • Well, what problem using array of structures make? About the boxing, I'm assuming that you rarely need 'the raw pointer' to the object, so it's not much of a concern. :)
    – arul
    Commented May 16, 2011 at 2:35
  • 1
    @arul: The array is indeed blittable by itself, but the struct isn't.
    – user541686
    Commented May 16, 2011 at 3:02

Not the answer you're looking for? Browse other questions tagged or ask your own question.