Reputation: 1369
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.
Upvotes: 3
Views: 2289
Reputation: 1063013
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:
fixed
- you are just accessing it as a pointer directly (via ldloca
)fixed
, to pin the object in place for the duration of your manipulationsref 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 anythingsomeArray[8]
(since you can manipulate the contents of arrays in-situ)Upvotes: 6
Reputation: 283694
Your question doesn't make any sense. You're confusing arrays with pointers, probably because in C and C++, and array quickly degrades to a pointer in most contexts, including the []
subscript operator.
But this is C#. Arrays and pointers are completely separate beasts (although you can coerce an array to a pointer, you have to do so in a fixed
statement, in order to make sure the pointer remains valid). You should be comparing
{
RunBlock_t r = new RunBlock_t();
for (int i=0; i<512; i++) r.raw[i]=0;
}
to
RunBlock_t r;
{
r = new RunBlock_t();
for (int i=0; i<512; i++) r.raw[i]=0;
}
where both use arrays. Or else
{
RunBlock r = new RunBlock_t();
fixed (byte* ptr = r.raw) for (int i=0; i<512; i++) ptr[i]=0;
}
to
RunBlock_t r;
{
r = new RunBlock_t();
fixed (byte* ptr = r.raw) for (int i=0; i<512; i++) ptr[i]=0;
}
where both use pointers.
And then you will see that the scope the variable is declared in has absolutely no relationship to needing fixed
.
Upvotes: 5