The 'fixed' statement in C# and managed pointer in CIL code
Asked Answered
S

3

7

In unsafe code in C#, I assigned a pointer to the managed variable of an array type:

int[] array = new int[3];

// ...

fixed (int* ptr = array)
{
    // Some code
}

Then I looked at corresponding part of the IL code:

.locals init ([0] int32[] 'array',
       [1] int32& pinned ptr)

Since this is unsafe code, and int* ptr is declaration of unmanaged pointer (or I think so at the moment), why in the CIL code doesn't write int32* ptr, instead of int32& ptr?

Sold answered 25/6, 2011 at 20:54 Comment(0)
Q
4

http://www.ecma-international.org/publications/standards/Ecma-335.htm

Page 334

"1.1.5.2 Managed pointers (type &)

1.2 Managed pointers (&) can point to a local variable, a method argument, a field of an object, a field of a value type, an element of an array, a static field, or the address where an element just past the end of an array would be stored (for pointer indexes into managed arrays). Managed pointers cannot be null. (They shall be reported to the garbage collector, even if they do not point to managed memory)"

Page 149

7.1.2 pinned

The signature encoding for pinned shall appear only in signatures that describe local variables (§15.4.1.3). While a method with a pinned local variable is executing, the VES shall not relocate the object to which the local refers. That is, if the implementation of the CLI uses a garbage collector that moves objects, the collector shall not move objects that are referenced by an active pinned local variable. [Rationale: If unmanaged pointers are used to dereference managed objects, these objects shall be pinned. This happens, for example, when a managed object is passed to a method designed to operate with unmanaged data. end rationale]

I agree with Hans as to the rational behind the msil language design choice.


These two things are different:

int[] arry = new int[5];

fixed (int* ptr = arry)
{
  ...
}

vs.

int* ptr = stackalloc int[5];

If you look at the IL created for the second one, you'll see this (which I think is what you're expecting):

.locals init ([0] int32* ptr)

In the first version (your version), you're pointing to an instance of System.Array (a managed type). In my version (using stackalloc) you're pointing to what I think you're expecting to point to... a block of memory large enough for 5 ints.

Quiet answered 25/6, 2011 at 21:19 Comment(9)
I didn't understand the rationale: "If unmanaged pointers are used to dereference managed objects, these objects shall be pinned". In the code above the array is pinned by fixed statement and we are still using managed pointer?Sold
@Quiet Thank you for your time. I can notice that in your example with stackalloc int[5] we occupied memory on the stack, not on the heap, like with new int[5], so now this is not subject to garbage collection, and we don't need fixed statement, since C# compiler only lets you assign a pointer to a managed variable in a fixed statement. So, when I am assigning pointer to a managed variable in a fixed statement, that pointer is always declared as managed pointer? I can accept that :)Sold
@Vlad... you've got it. If you wouldn't mind checking the checkmark under the voting arrows, I'd be appreciative : )Quiet
@Quiet I already checked it 30 minutes ago or something, but don't go yet :). Just one more thing. I wanted to point something in your example int* ptr = stackalloc int[5]; In IL code you got .locals init ([0] int32* ptr) regardless of whether you assigned something to the declared variable ptr. If you wrote just int* ptr; you still get .locals init ([0] int32* ptr). And if you have reference to something on the garbage collected heap, you cannot just write int* ptr = new int[5]; you have to declare ptr and assign reference to ptr inside fixed statement.Sold
It is little clearer to me, but I just edited CIL code and write * instead of &, and code executed without complains. #%$%$@ I am going to sleep :)Sold
@Vladimir, I think you're not accepting answers correctly. Of the 5 questions you've asked, none are marked as "accepted". meta.stackexchange.com/questions/5234/…Quiet
@Quiet huh, I missed that completely, thank you, now I learnt something new about stackoverflow itself :) Hope I got it this time. Cheers!Sold
The above answer is great, correct, relevant info, but doesn't do anything to convey why pinning is a thing (which is why there's more than one type of pointer).Calcaneus
In .NET, in general, objects are allocated on the heap, structures are allocated on the stack (some exceptions). The CLR, during collection, analyzes stack frames and the heap (with all other threads suspended) for references/pinned pointers. If no refs are there, the object is safe to collect; if no pins are present, it's safe to move the object (i.e. to compact the heap). Without pinning, the data you're pointing at could move. Your 'fix' to edit IL to make the ptr native removes the pinning, GC can't know it's not safe to move. Unlikely you'll catch a problem in debug, but not 'safe'.Calcaneus
M
2

ILDASM was written by a C++ programmer at Microsoft. A language where the difference between pointers and references are a big deal. A C++ reference under the hood is also a pointer, but one that's guaranteed to never be null. A reference is syntactically identified by &, a pointer is *.

Which is the case here; there is a difference between the pointer and the pointed-to value. The pointed-to value may be null, but the reference to the pointer is never null. The "array" variable is guaranteed to be present in the stack frame and its reference thus have a non-null value. Only its value might be null. Which happens when the array isn't initialized. This level of indirection made pointers unpopular and largely absent in the C# language. And CS101.

Mastership answered 25/6, 2011 at 21:20 Comment(2)
Huh, I didn't understand: In CLR references to objects are one thing, and managed pointer is second, and unmanaged pointers are third. For example this unsafe c# code: int x; int* y; y=&x; Here in IL code we will have .locals init (int32* y) just like expected, and I can say that since I declared x, the &x is never null, and the value of x can be null, but I don't see the connection between that fact, and the way local variable y is declared as managed or unmanaged pointer?Sold
What is "CS101"? Computer science 101? A particular course?Colicroot
T
-5

C# is a managed coded language, so there will not be any pointers. But there is a wrapper class to use the pointers. Maybe because of that, you are noticing some difference in these two.

Taylor answered 25/6, 2011 at 21:21 Comment(2)
What do you mean by "a wrapper class to use the pointers"? Can you elaborate?Colicroot
OK, the OP has left the building ("Last seen more than 4 years ago.").Colicroot

© 2022 - 2024 — McMap. All rights reserved.