How to implement a platform-independent garbage collector? - c

Basically, I'm interested in writing a platform-independent garbage collector in C, probably using the mark-and-sweep algorithm or one of its common variants. Ideally, the interface would work along the following lines:
(1) gc_alloc() allocates memory
(2) gc_realloc() reallocates memory
(3) gc_run() runs the garbage collector.
I've already taken a look at the libgc garbage collection library developed by Boehm et. al., but it isn't platform-independent; it's just been ported to many different systems. I'd like to implement a garbage collector that contains no system-dependent code. Speed isn't a huge issue.
Any suggestions?

Unfortunately, it's not really possible to make a truly platform-independent garbage collector in C. A strict reading of the C standard allows any type (except unsigned char) to have trap bits - bits which, when they have the wrong value, result in the system signalling an exception (ie, undefined behavior). When scanning allocated blocks for pointers, you have no way of determining whether a particular block of memory contains a legal pointer value, or if it'll trap as soon as you try to look at the value in it.
Examining pointers as ints doesn't help either - no int type is required to have representation compatible with a pointer. intptr_t is only available on very recent compilers, and I don't believe its representation is required to be compatible either. And ints can have trap bits as well.
You also don't know the alignment requirements of pointers. On a platform where pointers have no alignment requirements (ie, can start at any byte) this means you need to stop at every byte, memcpy to a suitable pointer type, and examine the result. Oh, and different pointer types can have different representations as well, which is also unavoidable.
But the bigger issue is finding the root set. Bohem GC and the others tend to scan the stack, as well as static data, for pointers that should go in the root set. This is impossible without knowledge of the OS's memory layout. So you will need to have the user explicitly mark members of the root set, which sort of defeats the point of a garbage collector.
So, in short, you can't make a GC in truly portable C. In principle you can if you make a few assumptions:
Assume the root set will be explicitly given to you by the user.
Assume there are no trap bits in pointer or int representations.
Assume intptr_t is available or assume all void *s are strictly ordered (ie, < and > work reasonably with pointers from different mallocations)
Assume all data pointer types have representation compatible with void *.
Optional, but gives a big speed boost: Hardcode the alignment of pointers (this is far from universal, and will need to be compiler- and platform-specific) This assumption will let you skip memcpying pointers to a known-aligned location, and will also cut down in the number of potential pointers to examine.
If you make these assumptions you should be able to make a conservative mark-sweep allocator. Use a binary tree to hold information on where allocations are, and scan over every possible aligned pointer location in allocated blocks for pointers. However, the need to explicitly provide the root set will make this all pointless - it'll be malloc and free all over again, except that for a certain ill-defined set of objects you can skip it. Not exactly what GC is supposed to provide, but I suppose it might have its place as, eg, part of a virtual machine (in which case the root set would be derived from information available to the virtual machine).
Note that this all applies only to conservative GCs - that is, ones which work blindly, scanning for pointers in data without knowing where it could be. If you're working on a VM, it's a lot easier - you can build a unified data type for all allocations by the VM that explicitly lists where pointers can be found. With this plus an explicit root set, you can build a non-conservative GC; this should be sufficient for building a VM or an interpreter.

For a mark-and-sweep algorithm, all you really need to do is calculate which objects are reachable from the root set, right? (It was a while ago since I dug into this...)
This could be managed by a separate object graph for GC-managed objects, and "all" you would need to do is add functions to properly manage that graph whenever you allocate or modify managed objects.
If you also add reference counting for managed objects you would make it easier to calculate which ones are reachable directly from stack references.
This should probably be possible to write fairly platform-independent, though it might be arguable whether this would be a true garbage collector.
Simple pseudocode to show what I mean by reference counting and graph management:
some_object * pObject = gc_alloc(sizeof(some_object));
some_container * pContainer = gc_alloc(sizeof(some_container));
pContainer->pObject = pObject;
/* let the GC know that pContainer has a reference to pObject */
gc_object_reference(pContainer, pObject);
/* a new block of some kind */
{
/* let the GC know we have a new reference for pObject */
some_object * pReference = gc_reference(pObject);
/* do stuff */
...
/* let the GC know that this reference is no longer used */
gc_left_scope(pReference);
}
gc_left_scope(pObject);
gc_run(); /* should not be able to recycle anything, since there still is a live
* reference to pContainer, and that has a reference to pObject
*/

Related

Filler at the end of a struct for future growth

This document called "Good Practices in Library Design, Implementation, and Maintenance" by Ulrich Drepper says (bottom of page 5):
[...] the type definition should always create at least a minimal
amount of padding to allow future growth
[...]
Second, a structure should contain at the end a certain number of fill bytes.
struct the_struct
{
int foo;
// ...and more fields
uintptr_t filler[8];
};
[...]
If at a later time a field has to be added to the structure the type definition can be changed to this:
struct the_struct
{
int foo;
// ...and more fields
union
{
some_type_t new_field;
uintptr_t filler[8];
} u;
};
I don't see the point of adding this filler at the end of the structure. Yes it means that when adding a new field (new_field) to the structure, it doesn't actually grow. But isn't the whole point of adding new fields to a structure that you didn't knew you were going to need them? In this example, what if you want to add not one field but 20? Should you then use a filler of 1k bytes just in case? Also, why does is it important that the size of a struct doesn't change in subsequent versions of a library? If the library provides clean abstractions, that shouldn't matter right? Finally, using a 64 bytes filler (8 uintpr_t (yes, it's not necessarily 64 bytes)) sounds like a waste of memory...
The document doesn't go into the details of this at all. Would you have any explanations to why this advice "adding fillers at the end of struct to plan for future growth" is a good one?
Depending on circumstances, yes, the size of the structure can be important for binary compatibility.
Consider stat(). It's typically called like this:
struct stat stbuf;
int r = stat(filename, &stbuf);
With this setup, if the size of the stat structure ever changes, every caller becomes invalid, and will need to be recompiled. If both the called and the calling code are part of the same project, that may not be a problem. But if (as in the case of stat(), which is a system call into the Unix/Linux kernel) there are lots and lots of callers out there, it's practically impossible to force them all to recompile, so the implication is that the size of the stat structure can never be changed.
This sort of problem mainly arises when the caller allocates (or inspects/manipulates) actual instances of the structure. If, on the other hand, the insides of the structure are only ever allocated and manipulated by library code -- if calling code deals only with pointers to the struct, and doesn't try to interpret the pointed-to structures -- it may not matter if the structure changes.
(Now, with all of that said, there are various other things that can be done to mitigate the issues if a struct has to change size. There are libraries where the caller allocates instances of a structure, but then passes both a pointer to the structure, and the size of the structure as the caller knows it, down into the library code. Newer library code can then detect a mismatch, and avoid setting or using newer fields which an older caller didn't allocate space for. And I believe gcc, at least, implements special hooks so that glibc can implement multiple versions of the same structure, and multiple versions of the library functions that use them, so that the correct library function can be used corresponding to the version of the structure that a particular caller is using. Going back to stat(), for example, under Linux there are at least two different versions of the stat structure, one which allocates 32 bits for the file size and one which allocates 64.)
But isn't the whole point of adding new fields to a structure that you
didn't knew you were going to need them?
Well yes, if you knew all along that you would need those members, then it would be counter-productive to intentionally omit them. But sometimes you indeed discover only later that you need some additional fields. Drepper's recommendations speak to ways to design your code -- specifically your structure definitions -- so that you can add members with the minimum possible side effects.
In this example, what if you
want to add not one field but 20?
You don't start out saying "I'm going to want to add 20 members". Rather, you start out saying "I may later discover a need for some more members." That's a prudent position to take.
Should you then use a filler of 1k
bytes just in case?
That's a judgment call. I recon that a KB of extra space in the structure definition is probably overkill in most cases, but there might be a context where that's reasonable.
Also, why does is it important that the size of a
struct doesn't change in subsequent versions of a library? If the
library provides clean abstractions, that shouldn't matter right?
How important it is that the size remains constant is a subjective question, but the size is indeed relevant to binary compatibility for shared libraries. Specifically, the question is whether I can drop a new version of the shared lib in place of the old one, and expect existing programs to work with the new one without recompilation.
Technically, if the definition of the structure changes, even without its size changing, then the new definition is incompatible with the old one as far as the C language is concerned. In practice, however, with most C implementations, if the structure size is the same and the layout does not change except possibly within previously-unused space, then existing users will not notice the difference in many operations.
If the size does change, however, then
dynamic allocation of instances of the structure will not allocate the correct amount of space.
arrays of the structure will not be laid out correctly.
copying from one instance to another via memcpy() will not work correctly.
binary I/O involving instances of the structure will not transfer the correct number of bytes.
There are likely other things that could go wrong with a size change that would (again, in practice) be ok under conversion of some trailing padding into meaningful members.
Do note: one thing that might still be a problem if the structure members change without the overall size changing is passing structures to functions by value and (somewhat less so) receiving them as return values. A library making use of this approach to provide for binary compatibility would do well to avoid providing functions that do those things.
Finally, using a 64 bytes filler (8 uintpr_t (yes, it's not
necessarily 64 bytes)) sounds like a waste of memory...
In a situation in which those 64 bytes per structure is in fact a legitimate concern, then that might trump binary compatibility concerns. That would be the case if you anticipate a very large number of those structures to be in use at the same time, or if you are extremely memory-constrained. In many cases, however, the extra space is inconsequential, whereas the extra scope for binary compatibility afforded by including padding is quite valuable.
The document doesn't go into the details of this at all. Would you
have any explanations to why this advice "adding fillers at the end of
struct to plan for future growth" is a good one?
Like most things, the recommendation needs to be evaluated relative to your particular context. In the foregoing, I've touched on most of the points you would want to consider in such an evaluation.

Finding roots for garbage collection in C

I'm trying to implement a simple mark and sweep garbage collector in C. The first step of the algorithm is finding the roots. So my question is how can I find the roots in a C program?
In the programs using malloc, I'll be using the custom allocator. This custom allocator is all that will be called from the C program, and may be a custom init().
How does garbage collector knows what all the pointers(roots) are in the program? Also, given a pointer of a custom type how does it get all pointers inside that?
For example, if there's a pointer p pointing to a class list, which has another pointer inside it.. say q. How does garbage collector knows about it, so that it can mark it?
Update: How about if I send all the pointer names and types to GC when I init it? Similarly, the structure of different types can also be sent so that GC can traverse the tree. Is this even a sane idea or am I just going crazy?
First off, garbage collectors in C, without extensive compiler and OS support, have to be conservative, because you cannot distinguish between a legitimate pointer and an integer that happens to have a value that looks like a pointer. And even conservative garbage collectors are hard to implement. Like, really hard. And often, you will need to constrain the language in order to get something acceptable: for instance, it might be impossible to correctly collect memory if pointers are hidden or obfuscated. If you allocate 100 bytes and only keep a pointer to the tenth byte of the allocation, your GC is unlikely to figure out that you still need the block since it will see no reference to the beginning. Another very important constraint to control is the memory alignment: if pointers can be on unaligned memory, your collector can be slowed down by a factor of 10x or worse.
To find roots, you need to know where your stacks start, and where your stacks end. Notice the plural form: each thread has its own stack, and you might need to account for that, depending on your objectives. To know where a stack starts, without entering into platform-specific details (that I probably wouldn't be able to provide anyways), you can use assembly code inside the main function of the current thread (just main in a non-threaded executable) to query the stack register (esp on x86, rsp on x86_64 to name those two only). Gcc and clang support a language extension that lets you assign a variable permanently to a register, which should make it easy for you:
register void* stack asm("esp"); // replace esp with the name of your stack reg
(register is a standard language keyword that is most of the time ignored by today's compilers, but coupled with asm("register_name"), it lets you do some nasty stuff.)
To ensure you don't forget important roots, you should defer the actual work of the main function to another one. (On x86 platforms, you can also query ebp/rbp, the stack frame base pointers, instead, and still do your actual work in the main function.)
int main(int argc, const char** argv, const char** envp)
{
register void* stack asm("esp");
// put stack somewhere
return do_main(argc, argv, envp);
}
Once you enter your GC to do collection, you need to query the current stack pointer for the thread you've interrupted. You will need design-specific and/or platform-specific calls for that (though if you get something to execute on the same thread, the technique above will still work).
The actual hunt for roots starts now. Good news: most ABIs will require stack frames to be aligned on a boundary greater than the size of a pointer, which means that if you trust every pointer to be on aligned memory, you can treat your whole stack as a intptr_t* and check if any pattern inside looks like any of your managed pointers.
Obviously, there are other roots. Global variables can (theoretically) be roots, and fields inside structures can be roots too. Registers can also have pointers to objects. You need to separately account for global variables that can be roots (or forbid that altogether, which isn't a bad idea in my opinion) because automatic discovery of those would be hard (at least, I wouldn't know how to do it on any platform).
These roots can lead to references on the heap, where things can go awry if you don't take care.
Since not all platforms provide malloc introspection (as far as I know), you need to implement the concept of scanned memory--that is, memory that your GC knows about. It needs to know at least the address and the size of each of such allocation. When you get a reference to one of these, you simply scan them for pointers, just like you did for the stack. (This means that you should take care that your pointers are aligned. This is normally the case if you let your compiler do its job, but you still need to be careful when you use third-party APIs).
This also means that you cannot put references to collectable memory to places where the GC can't reach it. And this is where it hurts the most and where you need to be extra-careful. Otherwise, if your platform supports malloc introspection, you can easily tell the size of each allocation you get a pointer to and make sure you don't overrun them.
This just scratches the surface of the topic. Garbage collectors are extremely complex, even when single-threaded. When you add threads to the mix, you enter a whole new world of hurt.
Apple has implemented such a conservative GC for the Objective-C language and dubbed it libauto. They have open-sourced it, along with a good part of the low-level technologies of Mac OS X, and you can find the source here.
I can only quote Hot Licks here: good luck!
Okay, before I go even further, I forgot something very important: compiler optimizations can break the GC. If your compiler is not aware of your GC, it can very well never put certain roots on the stack (only dealing with them in registers), and you're going to miss them. This is not too problematic for single-threaded programs if you can inspect registers, but again, a huge mess for multithreaded programs.
Also be very careful about the interruptibility of allocations: you must make sure that your GC cannot kick in while you're returning a new pointer because it could collect it right before it is assigned to a root, and when your program resumes it would assign that new dangling pointer to your program.
And here's an update to address the edit:
Update: How about if I send all the pointer names and types to GC when
I init it? Similarly, the structure of different types can also be
sent so that GC can traverse the tree. Is this even a sane idea or am
I just going crazy?
I guess you could allocate our memory then register it with the GC to tell it that it should be a managed resource. That would solve the interruptability problem. But then, be careful about what you send to third-party libraries, because if they keep a reference to it, your GC might not be able to detect it since they won't register their data structures with your GC.
And you likely won't be able to do that with roots on the stack.
The roots are basically all static and automatic object pointers. Static pointers would be linked inside the load modules. Automatic pointers must be found by scanning stack frames. Of course, you have no idea where in the stack frames the automatic pointers are.
Once you have the roots you need to scan objects and find all the pointers inside them. (This would include pointer arrays.) For that you need to identify the class object and somehow extract from it information about pointer locations. Of course, in C many objects are not virtual and do not have a class pointer within them.
Good luck!!
Added: One technique that could vaguely make your quest possible is "conservative" garbage collection. Since you intend to have your own allocator, you can (somehow) keep track of allocation sizes and locations, so you can pick any pointer-sized chunk out of storage and ask "Might this possibly be a pointer to one of my objects?" You can, of course, never know for sure, since random data might "look like" a pointer to one of your objects, but still you can, through this mechanism, scan a chunk of storage (like a frame in the call stack, or an individual object) and identify all the possible objects it might address.
With a conservative collector you cannot safely do object relocation/compaction (where you modify pointers to objects as you move them) since you might accidentally modify "random" data that looks like an object pointer but is in fact meaningful data to some application. But you can identify unused objects and free up the space they occupy for reuse. With proper design it's possible to have a very effective non-compacting GC.
(However, if your version of C allows unaligned pointers scanning could be very slow, since you'd have to try every variation on byte alignment.)

What non-NULL memory addresses are free to be used as error values in C?

I need to write my own memory allocation functions for the GMP library, since the default functions call abort() and leave no way I can think of to restore program flow after that occurs (I have calls to mpz_init all over the place, and how to handle the failure changes based upon what happened around that call). However, the documentation requires that the value the function returns to not be NULL.
Is there at least one range of addresses that can always be guaranteed to be invalid? It would be useful to know them all, so I could use different addresses for different error codes, or possibly even different ranges for different families of errors.
If the default memory allocation functions abort(), and GMP's code can't deal with a NULL, then GMP is likely not prepared to deal with the possibility of memory allocation failures at all. If you return a deliberately invalid address, GMP's probably going to try to dereference it, and promptly crash, which is just as bad as calling abort(). Worse, even, because the stacktrace won't point at what's really causing the problem.
As such, if you're going to return at all, you must return a valid pointer, one which isn't being used by anything else.
Now, one slightly evil option would be to use setjmp() and longjmp() to exit the GMP routines. However, this will leave GMP in an unpredictable state - you should assume that you can never call a GMP routine again after this point. It will also likely result in memory leaks... but that's probably the least of your concerns at this point.
Another option is to have a reserved pool in the system malloc - that is, at application startup:
emergencyMemory = malloc(bignumber);
Now if malloc() fails, you do free(emergencyMemory), and, hopefully, you have enough room to recover. Keep in mind that this only gives you a finite amount of headroom - you have to hope GMP will return to your code (and that code will check and see that the emergency pool has been used) before you truly run out of memory.
You can, of course, also use these two methods in combination - first use the reserved pool and try to recover, and if that fails, longjmp() out, display an error message (if you can), and terminate gracefully.
No, there isn't a portable range of invalid pointer values.
You could use platform-specific definitions, or you could use the addresses of some global objects:
const void *const error_out_of_bounds = &error_out_of_bounds;
const void *const error_no_sprockets = &error_no_sprockets;
[Edit: sorry, missed that you were hoping to return these values to a library. As bdonlan says, you can't do that. Even if you find some "invalid" values, the library won't be expecting them. It is a requirement that your function must return a valid value, or abort.]
You could do something like this in globals:
void (*error_handler)(void*);
void *error_data;
Then in your code:
error_handler = some_handler;
error_data = &some_data;
mpz_init(something);
In your allocator:
if (allocated_memory_ok) return the_memory;
error_handler(error_data);
abort();
Setting up the error handler and data before calling mzp_init might be somewhat tedious, but depending how different the behaviour is in different cases, you might be able to write some function or macro to deal with it.
What you can't do, though, is recover and carry on running if the GMP library isn't designed to cope after an allocation fails. You're at the mercy of your tools in that respect - if the library call doesn't return on error, then who knows what broken state its internals will be left in.
But that's a fully general view, whereas GMP is open source. You can find out what actually happens in mpz_init, at least for a particular release of GMP. There might be some way to ensure in advance that your allocator has enough memory to satisfy the request(s), or there might be some way to wriggle out without doing too much damage (like bdonlon says, a longjmp).
Since nobody has provided the correct answer, the set of non-NULL memory addresses you can safely use as error values is the same as the set of addresses you create for this purpose. Simply declare a static const char (or global const char if you need it to be globally visible) array whose size N is the number of error codes you need, and use pointers to the N elements of this array as the N error values.
If your pointer type is not char * but something else, you may need to use an object of that type instead of a char array, since converting these char pointers into another pointer type is not guaranteed to work.
Only garanteed on current main stream operating systems (with enabled virtual memory) and CPU architectures:
-1L (means all bits on in a value large enough for a pointer)
This is used by a lot of libraries to mark pointers which are freed. With this you can find out easily if the error cames from using a NULL pointer or a hanging reference.
Works on HP-UX, Windows, Solaris, AIX, Linux, Free-Net-OpenBSD and with i386, amd64, ia64, parisc, sparc and powerpc.
Think this works enough. Don't see any reason for more then this two values (0,-1)
If you only return e.g. 16-bit or 32-bit aligned pointers, an uneven pointer-address (LSB equal to 1) will be at least "mysterious", and would create an opportunity for using my all-time favorite bogus-value 0xDEADBEEF (for 32-bit pointers) or 0xDEADBEEFBADF00D (for 64-bit pointers).
There are several ranges you can use, they are operating system and architecture specific.
Typically most platforms will reserve the first page (usually 4K bytes in length), to catch dereferencing of null pointers (plus room for a slight offset).
You can also point to the reserved operating system pages, on Linux these occupy the region from 0xc0000000 to 0xffffffff (on a 32 bit system). From userspace you won't have necessary privileges to access this region.
Another option (if you want to allocate several such values, is to allocate a page without read or write permissions using mmap or equivalent, and use offsets into this page for each distinct error value.
The simplest solution, is just to use either values immediately negative to 0, (-1, -2, etc.), or immediately positive (1, 2, ...). You can be very certain these addresses are on inaccessible pages.
A possibility is to take C library addresses that are guaranteed to exist and that thus will never be returned by malloc or similar. To be most portable this should be object pointers and not function pointers, but casting ((void*)main) would probably be ok on most architectures. One data pointer that comes to my mind is environ, but which is POSIX, or stdin etc which are not guaranteed to be "real" variables.
To use this you could just use the following:
extern char** environ; /* guaranteed to exist in POSIX */
#define DEADBEAF ((void*)&environ)

pointer vs handles in C (are the terms used to convey separate things?)

Recently, I read a white paper by an individual who refers to a pointer to a struct as a handle. The author was clearly someone who had written C code on the windows platform previously. Googling indicates that windows programmers interact with system components via handles. I am wondering if it is common practice for windows programmers to refer to all struct pointers as handles? Or is the term handle meant to convey something beyond pointer to struct? I am asking as a linux C programmer.
The white paper I am referring to is:
Duff, Heroux, and Pozo. An Overview of the Sparse Basic Linear Algebra Subprograms: The New Standard from the BLAS Technical Forum. ACM Transactions on Mathematical Software, Vol 28, No. 2, June 2002, Pages 239-267.
The term handle generally means some opaque value that has meaning only to the API which produced it. In Win32, the HANDLE type is either a pointer in kernel memory (which applications cannot access anyway) or an index into some kernel-internal array.
A handle is an old and revered concept.
A cookie is much the same thing. Or a GUID. Or a ticket to retrieve your car from a car park, or your coat from a fancy restaurant, etc.
Its any unique value that when presented back to the issuer can be used to track back to the actual thing referred, by whatever opaque mechanism the issuer wants. You may or may not know anything about that process, nor what the underlying thing is, exactly (only conceptually).
It was heavily used by Windows, but it is certainly not unique to Windows.
You would not normally use "handle" to mean "pointer to struct." Handle is more like "token" than like "pointer." It refers to something - file, system resource, memory, state-snapshot, etc. But what-exactly-it-is is based on the context of the handle itself (i.e. who issued the handle).
Handles were also used heavily in early filesystem programming in K&R C.
I use the word handle to mean a pointer that points to an "object" that represents a resource - often an OS resource, whereas a pointer just points to some memory. If you have a handle to something, you shouldn't try to read and write bytes into it directly, but manipulate it through provided methods.
Often handles are implemented as an opaque void *, which is further encouragement not to try to directly dereference it.
Since you refer to handles being used as a pointer to a structure, as used by a Windows programmer, I'll answer within that context. Please note that there are clearly many different kinds of "handles", as it is a generic concept widely used within the computing environment. Certainly you will be familiar with the concept of a file handle; Windows also offers window handles and many other kinds of handles. Having said that:
A "memory handle" (that is similar to a pointer to a struct) is a concept from the land of 16-bit Windows programming, where there was no memory manager in the CPU and all memory management had to be done in software. Essentially, a "handle" was sort of a pointer, but the OS would be free to move around the memory that the handle referred to. You can't do that with a regular pointer, but the handle had functions that would get and release the actual memory address.
With the introduction of Win32, where the CPU had a hardware memory manager, the concept of the memory handle became obsolete. Other types of handles such as file handles and window handles still exist in Win32, but are not pointers to structs.
The term handle is used to mean any technique that lets you access to another object. A handle can be a pointer, a reference, a pointer to a pointer, etc.. But sure its related to classes, objects etc. So handle need not always be a pointer to structure.
-AD.
In the old days of MacOS programming, back before OSX, a handle was a pointer to a pointer. That allowed the OS to move things around without invalidating the user's pointers. There were rules on when we could assume the pointed-to object wouldn't move, which I don't remember.
The term "handle" didn't orignate on Windows, though it became widespread among Windows programmers.
In the C standard library (stdio.h), file handles are pointers to a data structure used by the C library.
Pure Unix programming uses file descriptors, which are indexes into a kernel data structure, but pointers have been used as handles in Unix for over 30 years.
"Handle" is a logical term, not a physical one. It's meant as a proxy to a physical object to code that has more intimate knowledge of the object. A pointer to a struct is one such proxy, but there are many other possibilites.
No, it is not particularly common amongst Windows programmers to refer to pointers as handles, but doing so isn't WRONG either. The term "handle" is usually used to describe something you use to access something through, and in that sense are all pointers handles (but not all handles are pointers). Win32's handles are AFAIK usually not pointers, but instead indices to internal OS tables - but this might change in future versions of Windows.
A handle is a generic term for a reference (not specifically a C++ reference) to an object.
A pointer is a subset of handle, since it points to objects.
A foreign key in a database is also a handle, since it points to records in other tables; and it is not a pointer.
In the Windows API environment, they used the abstract term handle so they could use an integer into a table, a pointer, or other methods, without interfering with the client; IOW, defining an abstract interface.
In summary, a handle can be something other than a pointer, such as an integer index or an object containing more details about the object (such as a smart pointer).
I'm probably older than most of the respondents, having earned a living coding in C on both the early (late 80s) Macintosh and both 16 and 32-bit Windows. In those ancient times (when an IBM mainframe might have only 256k of memory) a handle was always a pointer (or table offset) to a memory pointer.
As a previous respondent mentioned, that allowed tables of pointers to memory blocks to be managed by the OS without invalidating the "handles" used by the programmer. Unfortunately, I do not remember how we guaranteed that an allocated block would not be moved while we using the handle.
Windows defines handles for many things. They're not necessarily pointers at all -- some are, but others are things like offsets into particular tables. A few are intentionally obfuscated. There are handles for everything from windows to device contexts to bitmaps, and so on.
In any case, a handle is normally intended as an opaque data type -- i.e. you're not supposed to know anything about its value, only a set of predefined operations that can use it to accomplish various tasks. I believe C++/CLI also has a pointer-like object that's called a handle. I believe it's supposed to be closer to an opaque data type though -- if memory serves, you're not allowed to do any pointer arithmetic on them.
Handles are generally pointers that you don't directly need to dereference. Rather you pass them to API calls which operate on the underlying structs.
Historically on Windows, handles were not pointers. You would lock the handle to get a pointer before using it, and unlock it when you were done (and the pointer would become invalid). In the days before paged memory, old-school Windows did it's own memory management by swapping out resources only referenced by handles and swap them back in when they got locked. In practice, this made memory management a nightmare, but allowed Windows to simulate virtual memory on systems without hardware support for it.
A pointer is definitely different than a handle. A pointer is an address of something unspecified in memory. A pointer to a structure can be called a "handle" (usually by using 'typedef').
A handle is a concept used in writing the windows operating system.
A pointer is a part of the C language.
Actually a pointer is a variable which contains the address of another variable,but a handle is a pointer to a pointer i.e a pointer which contains the address of another pointer
FOR EX:
int x=10;
int *a=&x;// this is a simple pointer
int *b=a;// this is a handle
A handle is a number, the pointer is not a handle
// storage
char data[0xFFFF] = {0} ;
// pointer aka "iterator"
char * storage_pointer = & data[42];
// handle
size_t storage_handle = 42 ;
The key difference or if you prefer to call it the "advantage" of handles is one can try to deduce if the handle is valid or if you prefer the term, "dangling".
I do use handles whenever feasible. Here is a good article on advantages and implementation practices.

Why is it "impossible" to implement garbage collection in C because of weak typing?

I was told by a rather smart person that you cannot implement garbage collection in C because of it's weakly typed. The basic idea seems to be that C gives you too much freedom. He mentioned casting pointers without type checking...
I don't really grok the idea. Can someone give me an explanation and possibly a code sample of why this wouldn't work.
NOTE: Obviously C is about speed and why would you want to add garbage collection? I'm just curious really.
He probably referred to the fact that you can cast a pointer to an int and back to the original pointer type. It's pretty much impossible for a GC to clean up correctly when you do that, consider:
char * p = (char *) malloc(16);
int i = (int) p;
p = 0;
// GC runs and finds that the memory is no longer referenced
p = (char *) i;
// p is now a dangling pointer
EDIT: The above will only produce a dangling pointer with a precise GC. As others have pointed out, a conservative collector can still correctly handle this scenario as it assumes that any bit pattern that could be a valid pointer actually is a pointer and will thus not free the memory allocated. However, this is of course no longer possible when i is further modified such that it no longer looks like a valid pointer to the collector, e.g. as follows:
char * p = (char *) malloc(16);
int i = ~((int) p);
p = 0;
// GC runs and finds that the memory is no longer referenced
p = (char *) ~i;
// p is now a dangling pointer
Moreover, (again as others have pointed out) it's only impossible to implement a GC for C if you want to retain the full functionality of the language. If you refrain from using tricks like the above (i.e. you confine yourself to a subset of the possible operations) then GC is indeed feasible.
It's perfectly possible to implement whatever memory manager you can think of in C. The catch is that you then have to use its allocation/deallocation functions exclusively and restrict your 'pointer magic' to things it can keep track of. Aditionally, the memory management might be restricted to certain supported types.
For example, Objective-C's retain/release system and autorelease pools are basically memory managers implemented in C. Many libraries also implement their own, simple form of memory management like reference counting.
Then, there's the Boehm garbage collector. To use it, just replace your malloc()/realloc() calls with the Boehm versions and you never have to call free() again. Read about the possible issues with this approach.
Also, check this wikipedia page for a quick overview on how conservative garbage collectors work.
If you read the right papers and you have a bachelor's degree in CS, it's actually pretty easy to implement a decent conservative garbage collector for C---I have a dozen students who have done it as a class exercise taking about four weeks. Then spend 20 years improving it and you get the Boehm collector (libgc).
The basic idea is simple: if there is a bit pattern anywhere in a register, on the stack, in a global variable, or in a live heap object, and that bit pattern happens to be an address that falls inside an object allocated with malloc, than that object is considered live. Any object that is not live cannot possibly be reached by following pointers, and so it can be reclaimed and used to satisfy future allocation requests. This technique operates on the hardware representation of pointers, and it is completely independent of the type of pointer---types are irrelevant here.
It is true there is a caveat: conservative garbage-collection techniques can be fooled by willfully hiding pointers. Compress pointer-containing structures, keep the only copy of a pointer on disk, obfuscate a pointer by XORing 0xdeadbeef, and all these techniques will break a conservative collector. But this kind of problem is extremely rare unless done deliberately. Authors of optimizing compilers are usually careful not to hide pointers from such a collector.
The most interesting part of your question is why do it. Three reasons:
It eliminates the possibility of many memory-manangement bugs.
It simplifies your APIs because it is no longer necessary to specify who allocates memory, who owns the allocated memory, whether it's necessary to copy memory, and who is responsible for freeing memory.
Believe it or not, it can be faster than using malloc and free.
It's not impossible to implement a garbage collector for C (and in fact, they do exist, as a simple google search reveals), it's just difficult, because it can be difficult to determine if a certain string of bits is a pointer into an allocated block or just looks like one.
The reason this is an issue is because C (and C++, for that matter) allows you to cast from a pointer type to an integral type, so an integer variable might hold an address within an allocated block, preventing the GC from freeing that block, even though that value wasn't intended to be a pointer.
For example, let's say I have a block of memory allocated. Let's suppose this block of memory is allocated starting at address 0x00100000 (1,048,576), and is 1 MB long, so extends to 0x001FFFFF (2,097,151).
Let's say I also am storing the size of an image file in a variable (let's call it fileSize). This image file happens to be 1.5 MB (1,572,864 bytes).
So when the garbage collector runs, it will come across my fileSize variable, find it containing a value that corresponds to an address within my allocated block, and decide that it cannot free this block, lest it invalidate my maybe pointer. That's because the GC doesn't know if I've done this:
int fileSize;
{
char *mem = (char*)malloc(1048576);
fileSize = (int)(mem + 524288);
}
// say GC runs here
or if I've just done this:
int fileSize;
{
char *mem = (char*)malloc(1048576);
fileSize = 1572864;
}
// say GC runs here;
In the latter case, it is safe to free the block at *mem, (if no other references exist), whereas in the former, it's not. It must be conservative and assume that it's not, so the memory "leaks" (at least until fileSize goes out of scope or is changed to a value outside the allocated block).
But garbage collectors for C (and C++) do exist. Whether or not they are valuable is a matter for a different discussion.
The problem is that there's no way for the runtime to know for certain if any piece of memory is referenced or not. Even if you wrap all memory allocation in code that registers the usage, you can still obtain pointers to used memory via regular pointer manipulation (or by mistake). Casts only make the problem harder for the runtime. So if the runtime frees a piece of memory, it will mess things up for any pointers still pointing to that area of memory. Obviously the situation only gets worse when you consider that garbage collection has to work for multi-threaded applications as well.
It's impossible to implement a precise garbage collector for C because of the freedoms afforded C's pointers and the fact that the length of a C array is anyone's guess. This means a lot of sophisticated garbage collection approaches can't be used. (Copying and compacting garbage collectors come to mind.)
It is, however, possible to implement a conservative garbage collector (boehm), which basically assumes everything that looks like it might be a pointer is a pointer. This isn't very efficient, but it works for a suitably lenient definition of "works".
C is not weakly typed, but this code illustrates the difficulty in building a garbage collector into the language:
#include <stdio.h>
#include <stdlib.h>
int GetSomeMemory() {
char* pointerToHeapMemory = malloc(10);
return (int)pointerToHeapMemory;
}
int main() {
int memoryAddress = GetSomeMemory();
/* at this point a garbage collector might decide to clear up the memory that
* was allocated in GetSomeMemory on the grounds that pointerToHeapMemory
* is no longer in scope. But the truth is we still know about that memory and
* we're about to use it again... */
char* anotherPointerToHeapMemory = (char*) memoryAddress;
sprintf(anotherPointerToHeapMemory, "123456789\0");
printf("%s\n", anotherPointerToHeapMemory);
}
Garbage collection can be done so long as everyone working on a project agrees to avoid this kind of thing and use a common set of functions for allocating and accessing memory. For example, this is a C garbage collector implementation

Resources