DHO is Mostly Confused
Rants from Devon H. O'Dell
Pointers on Pointers
I’ve been hanging around in the ##c channel on Freenode (a channel for discussions about the C programming language) off-and-on for something like ten or fifteen years. One constant over that time (whatever my skill level) has been questions about pointers. They seem a topic that’s thoroughly confusing to many, especially neophyte hobbyists and autodidacts. Recently, I witnessed an explanation of pointers that involved people in small rooms, a man with a sign that points, and a magical great uncle named Merlin.
(I shit you not. No, I didn’t get it either.)
Once the person who was trying to learn was thoroughly confused, I said (tongue firmly in cheek), “Too much indirection.” Speaking indirectly about anything is confusing, especially when the thing you are trying to explain is indirection.
Much of the material I’ve seen on the Internet isn’t particularly great at teaching anything about pointers to anybody. People are already asking questions after having read their books. If 45 years of existence hasn’t yielded any obviously good methods for learning to use pointers, perhaps it’s not surprising that folks find them difficult to teach.
I’m intending for this to be part one of several posts on education about pointers. Here, I’ll discuss some (hopefully) best practices for teaching folks about them. Future posts will attempt to actually teach them.
Programming is based on abstractions. We use abstractions in an attempt to simplify, to avoid repetition, and to reuse code. This is great because we end up with reusable, generalized code – as long as we don’t overdo it. Comfort with indirection is a key differentiator between novice and expert programmers (indeed, so is overuse of indirection). Indirection is about representing something very complicated in a generic fashion, and this is a skill that is developed with experience.
Indirection is arguably the most powerful tool in software engineering. It is the engine that powers reusability, that drives well-designed software, that allows us to find patterns in complicated data, and then to derive a general solution for processing those data. This might be hyperbole, but there are few things more crucial to writing good software than a core understanding and appropriate use of abstraction.
The need for generality also isn’t immediately apparent to novices; they’ve little to no foundational knowledge upon which to build new knowledge. Thinking about abstraction in the way that skilled programmers do is not very obvious (though I believe most people do it in other ways). We need to start teaching people about indirection by first illustrating why generalization, abstraction, and reuse are important concepts when writing software.
But this isn’t always easy.
Indirection is difficult to talk about. First of all, it’s abstract. It’s difficult to discuss abstractions concretely. It’s difficult to discuss indirection directly. So we’ve invented tons of jargon to help us communicate effectively about it. Learning jargon is hard because definitions often seem circular, or at least as indirect and abstract as the concept they’re describing. For example, I’ve seen definitions of indirection that describe “going through” a variable to find a value. It makes sense once you know the thing, but it isn’t enlightening when you don’t.
Secondly, even if we did figure out some really good definitions, it’s unclear to me whether that would be helpful. We’d need to define numerous terms, and the person learning would have to learn them all individually before they stuck. Rote memorization is an outmoded, unhelpful teaching technique. And I believe it’s actually harmful here because it’s the critical thinking bit that’s important for appropriately using indirection and abstraction in software. Memorization neither reinforces nor encourages critical thinking. Far better would be for us to teach these concepts while also encouraging such problem-solving skills.
Even doing that, we’re faced with some additional problems.
Indirection in C is a shit show.
There’s too much crammed into a tight spot. If you learn variables first from a very correct resource, you know that a variable is a name for storage. But then you get to pointers, and it’s not clear why there’s any difference between a pointer and a variable (because the reason is hidden behind at least one level of indirection). You have the *
operator, which has three distinct meanings and has some special (possibly ambiguous-looking) behavior with the sizeof
operator.
If you learn about pointers, you’ve already learned about arrays. And the first thing you learn about pointers is that they’re actually quite similar to arrays. But then you get lost because you can’t spot what that similarity is other than some arithmetic that is somehow not regular. So you try to get some answers in ##c. Bad decision. Now four people are explaining it to you differently (and at length), and it’s not long until the explanation devolves into a lengthy and pointed argument about the word “decay” and whether it implies permanence.
It’s no wonder folks tear their hair out trying to learn this. How can we as educators and mentors do a better job teaching this subject to students?
Begin at their level. Knowledge is gained, stored, and retrieved by relating new information to past experience. Be curious about what your students and mentees already know. Don’t start off by correcting invalid assumptions, either. Try to ask “discovery questions.” These are questions intended to help you to discover where the student’s knowledge comes from (and where it starts to be incorrect) so you can start teaching from the most correct level.
When you start teaching pointers, begin by teaching their motivating uses. Why would we ever want pointers? They’re a tool for indirection, and abstraction is one of the most important things in software. But since the student is unlikely to be familiar with this either, we need to provide relevant examples of where generalization is helpful.
To do this, familiarize yourself with the individual’s current expertise. For example, if your student is comfortable with math, bring up addition. What are we actually doing when we add two numbers? How difficult would it be if the rules for obtaining a result changed for each set of numbers we had to add. If your student is comfortable with languages, bring up verb conjugation. How difficult would language be if every verb was irregular? Indirection and abstraction provide us a means to think about different things and create general rules for dealing with them.
Once you’ve established a motivation for pointers, indirection, and abstraction, make sure the student really understands variables. You’ll likely need to start at this level. Make sure the student understands what variables are, and all the information they describe. You can present it as a simple hierarchical set of definitions if you like:
- a variable is a convenient name for an object,
- an object is storage which may contain a value and has a lifetime,
- a value is information with a type,
- and a type is a way to understand how to interpret the information in the value.
In my experience, much of the confusion around pointers has to do with a poor understanding of how variables, storage, values, and types work. The above list is short enough to fit on a slide, and easy enough to memorize without being tedious.
Avoid using similies. Similies are indirect comparisons. When explaining abstraction and indirection, being indirect is about the worst way to do it. Use metaphor instead. If, for example, you were relating indirection in C to mail (because you first figured out your student understood mail relatively well), a pointer is not like an address, it is an address.
In the same vein, avoid using allegory unless you’ve given your story significant thought and the premise is already one your student understands. A poor story featuring magical men holding signs elucidates nothing.
I quite like using mail. Most people understand relatively well how mail works (snail or e), at least at a high level. We already have a shared term between pointers and mail: “address.” And it’s also very much true that writing a value to a pointer is analogous to sending a message to the address. Reading a value is very much analogous to receiving a message. I think this analogy ends up working well for other fields like concurrency, networking, and distributed systems, and the fundamental reason is because it describes a method of communication which is (if you squint a little) what we are doing when we write software. It’s nice to have a single thing to use to teach multiple concepts, both for you and the student.
One would think this would go without saying, but don’t berate the student. In ##C, we have a contributor who is very technically correct, and offers a lot of good advice. Unfortunately, this person has a habit of doing so in a sometimes cryptic fasion. This person also frequently assumes knowledge. The result is that whenever a newbie is unable to follow this person’s train of thought, they’re told in colorful ways what kind of idiot they must be.
Never do this; it is never helpful. If you don’t have the time, energy, or patience to help someone learn – which must necessarily start with figuring out where you should begin teaching – don’t do it. You will be frustrated, your student will be frustrated, and your student may lose interest in learning. Of course there is the class of person who only wants the solution book… but even here, the same applies. If you don’t want to help someone for any reason, just don’t.
Similarly, don’t assume that just because a student has a book, they should be able to learn from it. I’ve seen numerous people ask, “Where’s your book?” If it’s not the right book (how were they supposed to know beforehand?), they have to get another one. This isn’t great because most books on C aren’t particularly cheap, and this assumes an ability or desire to spend a possibly non-trivial amount of money. Secondly, even with the “right” book, it’s not guaranteed that everybody will come to the same understanding from the same material. Operating under the false assumption that they should be able to do this is likely to make your student feel stupid, which will close them to wanting to learn.
Learning is the process of forming new understanding. New understanding is, very fundamentally, the relation of new information to past experience. For any given static article, book, or tutorial about pointers, there will be some set of people who can not derive the indended meaning from that work, because it has no contextual relationship to their past experience. This is precisely what makes the real-time experience so exciting! As an educator, you have the ability (and the responsibility) to figure out where to start teaching. You get to be the material that is finally successful in helping this person learn something crucial. If this is not an exciting prospect to you, it might be worth reconsidering whether you are actually interested in helping folks learn.
If you’re not interested in putting in the effort, just don’t. You’re ruining the experience for the people who want to do it, and you’re ruining the experience for people who want to learn. You may even drive people away from learning, which is just an awful thing to do. And you are making it more difficult for people in the future to help your own student.
Through exercises in patience, curiosity, discovery, and compassion, we can help people learn (and be excited to learn) even the most complicated topics. Pointers are so difficult for so many people to learn that we as educators must engage in these exercises. We need to be patient in our explanation: knowledge can’t be rushed. We need to be curious about where our students are coming from, and discover where their gaps in understanding are. In doing this, we provide them personalized context for the new information, which allows them to engage in real learning. And in being compassionate, we recognize that we also don’t know everything, we didn’t always have our own knowledge, and we show our students that with just a little effort, they can be like us.