However, you have to declare an 'unsafe' context in which you'll do it. Unsafe contexts are tied to a local scope, which means that you can't do anything like having classes with fields of pointer types. Pointers don't outlive the function in which you use them.
Also, before you can get a pointer to an object you have to "pin" it, which tells the garbage collector not to move it around. This is necessary, of course, but it can have severe performance implications since it basically torpedoes all the garbage collector's best performance optimizations.
So pointers are there when you need them. . . but the language makes them extremely inconvenient to use them more than you absolutely need to. A pretty solid compromise, IMO.
Or are you just saying that you can 'use' them via out/ref? I interpreted your post to mean that you can store them in fields, which would be stellar but seems like it would break reference lifetime guarantees.
- Managed pointers can be passed as arguments, stored in local variables, and returned as values.
- Managed pointers cannot be stored in static variables, array elements, or fields of objects or value types.
So you're right, they have very limited scope as it'd be messy otherwise. It's my understanding that the JVM does not have pointers even at this level, hence Java can't do out/ref parameters. (Sure, tuples are a better way to handle many cases of out params, but sometimes a well chosen ref can really be nicer.)
I think C++ also uses the term "managed pointer" but not in the same technical sense (I think it just means reference.) Using "safe" C++/CLI generation limits the scope of pointers just like we'd expect.
You can if you mark the containing class/struct "unsafe", which allows both type safety and increased ease of use.
using System;
namespace Test2012
{
unsafe class C { public byte* P; public override string ToString( ) { return new IntPtr(P).ToString( ); } }
unsafe struct S { public byte* P; public override string ToString( ) { return new IntPtr(P).ToString( ); } }
static class Program
{
static unsafe void Main( )
{
fixed( byte* test = new byte[] { 1, 2, 3 } )
{
var c = new C( ) { P = test };
var s = new S( ) { P = test };
Console.WriteLine( "Test: {0}", c );
Console.WriteLine( "Test: {0}", s );
}
}
}
}
Result: Test: 40510364
Test: 40510364
Press any key to continue . . .Some of the things that break GC aren't even technically valid C code. For example, say I allocate three memory areas with malloc() then hold on only to the area with the lowest address, and reference the other two areas with offsets relative to the first area. This is illegal in C, though it will work on most existing implementations, because it's illegal to dereference a pointer past the end of an allocation.
Specific GC algorithms might not be able to handle say pointers into the interiors of objects, but that's not a general limitation for GC.
Yes, but there's a legal way to do this.
reference the other two areas with offsets relative to the first area
If you cast the pointers to uintptr_t, and perform your arithmetic on uintptr_t and cast your final pointer back (void * ) before using it, what you've done is perfectly legal and safe (albeit weird) since uintptr_t is an unsigned integer type with all the flexibility of unsigned integers, and if x = (uintptr_t)p then it's guaranteed that p == (void * )x.
In other words:
char * x = malloc(100);
char * y = malloc(100);
ptrdiff_t yminusx = y - x;
x[yminusx] = '\0'
is undefined behaviour, but char * x = malloc(100);
char * y = malloc(100);
uintptr_t yminusx = (uintptr_t)(y) - (uintptr_t)(x);
*(char *)(void *)((uintptr_t)(x) + yminusx) = '\0'
is valid C (and has the same meaning on a DWIM compiler).Quite correct, subject to the requirement that converting the same uintptr_t value back to (void ) will return the original pointer. So the conversion could be implemented as a hash table if the compiler author wanted.
But if you look carefully at what I wrote, I'm never converting a value which was not previously returned by a (void )->(uintptr_t) conversion.
1) Cast a pointer to an integer of sufficient size.
2) Xor that integer with another value.
3) Xor the result with the same value.
4) Cast back to a pointer.
5) Dereference.
It's sufficiently corner-case that I am not certain what the standard says about it. In any event, pointers matter when coding in assembly, where language standards aren't even relevant.Objective-C used to have a garbage collector, though (at least with regards to iOS) Apple removed this capability with the introduction or ARC. On the following URL is some discussion on the Objective-C garbage collector: http://cocoasamurai.blogspot.nl/2010/12/objective-c-memory-m...
http://www.cs.cornell.edu/courses/cs3110/2011sp/recitations/...
Oberon, Oberon-2 allows you to use no-GC pointers via the SYSTEM package.
Active Oberon additionally adds syntax support for untraced pointers.
Modula-3 has unsafe modules which allow you to use untraced pointers.
D has unsafe pointers in system blocks.
Ada and C++11 have support for optional GC defined on the language standard. However so far, there isn't any compiler vendor that really offers them.
In Java you can do a bit of dirty pointer tricks via the com.sun.unsafe package, but it is Oracle's VM specific. Although there are plans to make it part of the official library after Java 8.
Go has a GC and pointers, including internal pointers (e.g. to a field of a struct). Its GC started out fully conservative, then precise for the heap and conservative for the stack, and future versions will be fully precise.
In general, objects which can be moved or copied around by the runtime behind the programmer's back cannot have indefinite-extent hardware-address pointers taken directly into them. This includes objects managed by most halfway-decent garbage collectors (which tend to be implemented as stop-and-copy, not mark-and-sweep), but also includes such things as drawing surfaces in SDL and DirectDraw. Many runtimes take the C# approach -- provide a memory locking operation that fixes the object in memory while you do some pointer stuff on it and a dual unlocking operation that releases the object so it can be kicked around by the runtime again.
But also with a very small number of restrictions, you can make C have non-conservative GC.
ANSI C forbids aliasing as any type but a character. Furthermore, unions are to be interpreted as the last type they are accessed as.
The three places you run into issues are:
1) You can treat two structs that begin with the same values interchangeably so long as you only access the common beginning of the structures.
2) You can freely convert pointers to integers and back.
3) Storage returned by malloc() can be used heterogeneously (however, with strict aliasing rules, you can't switch types of the data once you've accessed it)
If you forbid those three things, then you can have a strict gc by adding runtime overhead to accessing data (once malloced data is accessed by a non-character type, you can treat it like that type forever).
However, lots and lots of code violates these rules, (the most famous code that does is probably the fast inverse square-root)[1]
http://betterexplained.com/articles/understanding-quakes-fas...