Requirement of 'fixed' seems inconsistent in C#

c#, fixed, pointers

Solution

You have, unfortunately, confused the question slightly. If I copy from the question exactly, then this works fine:

    {
        RunBlock_t r = new RunBlock_t();
        for (int i = 0; i < 512; i++) r.raw[i] = 0;
    }

and this:

    RunBlock_t r;
    {
        r = new RunBlock_t();
        fixed (byte* ptr = r.raw) for (int i = 0; i < 510; i++) ptr[i] = 0;
    }

raises:

You cannot use the fixed statement to take the address of an already fixed expression

And if we remove the `fixed`, it works.

What you should have shown was the function signature, i.e.

RunBlock_t r;
unsafe void Bar()
{
    {
        r = new RunBlock_t();
        fixed (byte* ptr = r.raw) for (int i = 0; i < 510; i++) ptr[i] = 0;
    }
}

Now the meaning becomes clearer. You see, when you use `fixed` to access a fixed-buffer in a value, you aren't actually fixing the buffer, nor are you fixing the value; what you are actually fixing is the containing object, i.e. the object that has the `r` field. This is to prevent GC from moving it around on the stack, which would be bad if we are accessing it as a pointer at the time. In our example above, our expression is really `fixed (byte* ptr = this.r.raw)`, and the thing that gets pinned is: `this`.

This is different if we only have a struct as a local. Locals are on the stack; they are (as the earlier message hinted) already fixed; the stack is never relocated by GC.

So:

- if you have a struct as a local variable, you do not need to use `fixed` - you are just accessing it as a pointer directly (via `ldloca`)

- if you have a struct that is a field in an object, you need to use `fixed`, to pin the object in place for the duration of your manipulations

- in cases where you're passing a reference to the struct (i.e. a `ref RunBlock_t` parameter), then you must use `fixed` just in case it is a field on an object; if the reference turns out to resolve to the stack, then it doesn't need to do anything

- note that everything here about "field on an object" also applies equally to "value in an array", i.e. if we are talking about `someArray[8]` (since you can manipulate the contents of arrays in-situ)

Problem

I have an ordered struct which contains fields.. ``` [StructLayout(LayoutKind.Explicit)] public unsafe struct RunBlock_t { [System.Runtime.InteropServices.FieldOffset(0)] public fixed byte raw[512]; } ``` If I declare this inside a function and want to use the pointer, it works fine.. ``` { RunBlock_t r = new RunBlock_t(); for (int i=0; i<512; i++) r.raw[i]=0; } ``` But if I declare the variable outside the scope, it requires a fixed implementation ``` RunBlock_t r; { r = new RunBlock_t(); fixed (byte* ptr = r.raw) for (int i=0; i<510; i++) ptr[i]=0; } ``` Why this difference in behavior? --- EDITED ----- Just want to state again that any other permutation does not work. ``` unsafe void foo() { RunBlock_t r = new RunBlock_t(); fixed (byte* ptr = r.raw) for (int i = 0; i < 512; i++) ptr[i] = 0; } ``` Generates You cannot use the fixed statement to take the address of an already fixed expression and does not compile. ``` RunBlock_t r; unsafe void foo() { r = new RunBlock_t(); for (int i=0; i<512; i++) r.raw[i]=0; } ``` Generates You cannot use fixed size buffers contained in unfixed expressions. Try using the fixed statement. and does not compile.

Original source