(Updated July 26, 2024)
Overview
I ask my students to write reflective journals throughout the semester to provide a mechanism of recognition about how they are (not) learning topics. This is a fantastic exercise to begin a deeper understanding of our minds. We are genuinely linking our thinking process to the details of how we make connections to more information. Understanding how we apply what we know and what we have recently learned to our solutions is something we need to cultivate. Some can easily recognize the ah-ha! moments and have an immediate moment of clarity for when the details finally make sense. Or when applying knowledge led them to a specific solution that eluded them. Others struggle to make those connections, and when the ah-ha! moment happens, they may not recognize it.
This is, of course, ok, and we need to be gentle and care for ourselves as we engage more fully in our learning process. Education is a journey that will provide years of a deeper understanding of the topics that interest us most. Take the time to reflect and enjoy the wins and losses. They are essential in equal measure.
If you’re not prepared to be wrong, you’ll never come up with anything original. – Sir Ken Robinson
My Own Journal Entry
The story of how I taught myself C is frequently shared in the classroom, and I use it as a tool for reflection on how we sometimes have trouble understanding how something works. Pointers in C gave me so much trouble in my early language studies. This concerned me since pointers in Pascal were so simple at the time.
It was about 1989 that I became serious about C programming language. I had dabbled a bit previously. My serious programming had been the UCSD Pascal environment on Apple computers in high school. I also learned BASIC on an Apple II, a DEC PDP-11/03, the Commodore VIC-20 and 64, and 6502/6510 assembly language on those Commodore machines. So, why was I having so much trouble with pointers in C?
It turns out I had an issue with the notation. For whatever reason, my brain did not like it. And that notation made comprehension of indirection complicated. This frustrated me because I had been using indirection on the MOS 6502 processor in assembly language for years! And, comparatively speaking, assembly language is supposed to be more challenging to grasp since everything is so primitive!
The bottom line was that I had to force myself to accept this is how it’s written. Stop whining and use it!
Need a picture? Ok, see below.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Function to populate the strings.
* Note the three stars!
*/
int populate(char ***theList, int count) {
for (int x=0; x < count; x++)
(*theList)[x] = strdup("The same thing.");
return 0;
}
int main(int argc, char *argv[]) {
char **list; // array of strings - note two stars.
int code;
// allocate twenty pointers to characters.
list = malloc(20 * sizeof(char*));
code = populate(&list, 20); // The & manifests the 3rd *
for ( int x = 0; x < 20; x++) {
printf("%s\n", list[x]); // <<-- where'd the stars go!?
free(list[x]);
}
free (list);
}
It's OK if none of that made any sense to you!
The three stars, or asterisks, represent levels of indirection. On the surface, it means a pointer to a pointer to a pointer to stuff. But its meaning can go much deeper.
More confusion enters into the equation when you realize that * (on the left) and [] (on the right) can be interchangeable - to some degree. This means that there are many ways to interpret the three asterisks.
As it turns out, this is where my problem was. Things made sense in Pascal due to the strict nature in which we could use pointers. We didn't have all of this flexibility. Now, don't get me wrong. I love the flexibility I have in C. It's fantastic! However, when you are just starting, it isn't very nice.
The Red October Moment
Wait a minute. We don't have to get the crew off the sub.
He would have had to do that. We just have to figure out what he's going to do.
How is he going to get them off the sub? They'd have to want to get off.
How do you get a crew to want to get off a submarine?
How do you get a crew to want to get off a nuclear sub...
Do you remember the scene in The Hunt for Red October when Captain Ramius (Sean Connery) wants to defect to America and bring his nuclear submarine with him? There's a soliloquy in the movie where Jack Ryan (Alec Baldwin) is trying to get into the mind of the ship's captain. He poses the problem to himself and begins to talk through the details:
- What do I know?
- What am I missing?
- Where do I find it?
I also refer to this as talking to the teddy bear. There is a book (I think it's Programming Pearls by Jon Bently) where they mention a school that has a TA available to programming students. However, between them and the TA is an anteroom with a formidable teddy bear. To get to the TA, you must first battle with Teddy. To accomplish this, you must explain your problem to the bear.
Slowly. Methodically. Like you are trying to teach someone else about your problem. On the surface, this seems ridiculous, even downright cruel - the TA's right over there! However, the idea is to slow down your mind by speaking the problem aloud and working through the details instead of jumping to the end and throwing your hands up. The fact that the recipient of your voice is a teddy bear helps to create the opportunity for the monologue. It's just you and your thoughts.
It works. Seriously.
My problem with triple-indirection happened at about the same time as the release of that movie. I did not appreciate the importance of that scene at the time, so my struggle continued.
Later in life, I realized I had one of those moments during that C project I'd worked on. I remembered talking to myself. My program blew up at runtime, I was upset, and the compiler kept saying things I didn't understand when I tried to fix it. The warnings and errors indicated I didn't know how to correct the problem. And when I did get it to compile, I'd run it, get a segmentation fault, and the program died.
Struggling and (still) confused, I had a mini-rant about pointers and dereferencing. As I told no one in particular, "I know the indirection's wrong. I'm giving you the array to populate, so what's your problem? Why do you keep dereferencing at that level? It's the wrong place!"
And then I realized what I'd done wrong. One specific change, a single keystroke, fixed the whole program, and I finally understood the three asterisks perfectly.
This is explained in the example above. Maybe that code makes more sense to you after reading this.
It's cool if it doesn't. That's the point.