I think one reason C pointers are difficult is that they conflate several concepts which are not really equivalent; yet, because they are all implemented using pointers, people can have a hard time disentangling the concepts.
In C, pointers are used to, amoung other things:
- Define recursive data structures
In C you'd define a linked list of integers like this:
struct node {
int value;
struct node* next;
}
The pointer is only there because this is the only way to define a recursive data structure in C, when the concept really has nothing to do with such a low-level detail as memory addresses. Consider the following equivalent in Haskell, which doesn't require use of pointers:
data List = List Int List | Null
Pretty straightforward - a list is either empty, or formed from a value and the rest of the list.
- Iterate over strings and arrays
Here's how you might apply a function foo
to every character of a string in C:
char *c;
for (c = "hello, world!"; *c != '\0'; c++) { foo(c); }
Despite also using a pointer as an iterator, this example has very little in common with the previous one. Creating an iterator that you can increment is a different concept from defining a recursive data structure. Neither concept is especially tied to the idea of a memory address.
Here is an actual function signature found in glib:
typedef struct g_list GList;
void g_list_foreach (GList *list,
void (*func)(void *data, void *user_data),
void* user_data);
Whoa! That's quite a mouthful of void*
's. And it's all just to declare a function that iterates over a list that can contain any kind of thing, applying a function to each member. Compare it to how map
is declared in Haskell:
map::(a->b)->[a]->[b]
That's much more straightforward: map
is a function that takes a function which converts an a
to a b
, and applies it to a list of a
's to yield a list of b
's. Just like in the C function g_list_foreach
, map
doesn't need to know anything in its own definition about the types to which it will be applied.
To sum up:
I think C pointers would be a lot less confusing if people first learned about recursive data structures, iterators, polymorphism, etc. as separate concepts, and then learned how pointers can be used to implement those ideas in C, rather than mashing all of these concepts together into a single subject of "pointers".