I don't buy it. Stress has nothing to do with getting kicked out of hack mode. Even the least stressful of interruptions, assuming it uses the local state registers that are used in hacking, can completely scramble your brain. The friendliest of comments from co-workers, assuming they're about hacking (but not your project) and require hacking thinking, can ruin it, if the comment turns into a conversation that lasts more than 60 seconds.
It's not about stress. It's about juggling a bunch of delicate state, and the state is being held in short-term memory registers that are easily clobbered.
That series of articles may be the most significant thing I read this year.
I have a comment on one bit:
In his article The Perils of JavaSchools, Joel Spolsky
refers to "programmers without the part of the brain
that does pointers or recursion". In this "ha ha only
serious" comment Spolsky displays a profound intuition.
He's obviously observed his teams very carefully. My
only disagreement with him is that I know from
experience that everyone has the necessary bit of the
brain. It's just that understanding pointers and
recursion requires juxtaposition, and in most people
that mode is not accessible.
The pointer problem
I am a programmer who tried and failed to get pointers many times over the course of some seven years, and then consciously tried a new way of thinking about it that flicked a switch in my mind nearly instantly. I was aware of the widespread belief that you either did or didn't get pointers, and immediately wrote down everything I could about the situation in order to capture it. I'll describe my findings and the situation here.
Pointers are easy when you think about the program in terms of memory and what's actually going on: casting of ints. The problem for me was that my brain was filtering out the correct scenario because due to a contradition in the syntax. Let me present a simple example to use for discussion.
One of the things that kept screwing me up with pointers in C was the contradiction in the situation where you pass a pointer to a function, yet the function receiving it has a different symbol for it. Look at the example above - where we declare 'c' in the parameter list for the function 'fn' it has a star next to it. Yet when we call fn it doesn't. But ... those are the same thing. How can that be?
I found that the more I thought about the problem the less I'd understand it. I tried to get several friends to step me through it and couldn't get there. I'd actually get it for a while, and then I'd lose it. In desperation - and I'm not making this up - I worked out a series of base examples that I knew to work even though I couldn't see why, printed them and pasted the sheets into the back of a copy of _Systems Programming for SVR4_. Horrifically embarassing - a professional unix-based programmer, who hangs around real geeks, who has designed and implemented several successful applications on a variety of platforms - reduced to this. I rapidly reached the point where I could write and debug C code without my colleagues detecting the weakness through my system. A key part of it was knowing the exact point at which I had to actively stop thinking about the syntax and trust what was written on my base examples.
OK - step back a bit. There's something else that's important. I'm told that I spoke early when I was a child, and rapidly got to the point where I could hold court in a room full of adults, and talking at their level. English was my strongest subject throughout high school. Throughout my life people around me seemed to think I was very smart but I felt like a fraud because although I was qite quick in language stuff, there were some problems that kids who struggled in general got quickly and that I didn't.
Early last year I was struggling through what felt like just such a problem and noticed myself feeling at a loss without an example to leverage. I wondered why it was that I needed to learn from example, and thought it interesting that some people are able to work through things without examples, yet I seemed incapable of it. This caused me to create a theory that I had a strong skill at certain sorts of learning based on pattern matching, and that overuse of this skill had stunted other learning skills. I told myself that I'd look out for opportunities to solve problems without using this kind of approach.
The first decent example that came up over the next few days was - conveniently - the pointer problem. A friend called me up to say she had an exam the next day and needed help with C - could I help? Readers will already recognise that are few times in any lifetime when good looking, charismatic, single girls (this one is a superb singer as well) will seek you out on the strength of your reputation for coding and I replied in a steady voice and pace, "Sure - why don't you come over now?" As I hung up I thought - oh oh - back to that old pointer problem that has been haunting me since the beginning of time. But then I realised that this was the opportunity I'd been looking for to overcome a problem with non-example based learning.
I had the whole problem solved less than five minutes from putting down the phone. I dug up my copy of _The C Programming Language_ and read the section on pointers to refamiliarise myself with the problem space. As you would know - the problem in a world without pointers is that you can't change non-global variables within a function so that the change persists past the end of the function (I had always been fine with this - nothing new so far). So I moved to the next step and decided to ask myself, "how would I solve this?" Easy - pass in the memory address. Have a mechanism for referring to memory addresses in memory instead of the variables they contain. Oh, and there needs to be some syntax to be able to dereference a variable.
That was the lightening bolt moment. The problem with pointers in C is that the [star][varname] syntax means two different things, and they are actually contradictory from a certain perspective. In one context it means "declare a variable that is a pointer" and in the other it means "deference". Whenever my languages-brain bit looked at this it couldn't deal with it, recognised it as a contradictory positions and basically shut me down, but not in a way that let me work out what was going on. Have a look at it yourself from that perspective - hopefully you'll see what I always did. Now whenever I look at C code I instead just think "everything is an int and pointers are all about casting ints, and a star when you declare something is your way of telling the compiler that the type of this thing is int-pointer instead of just int". Thus I now have no difficulties at all.
When I was at uni I heard the whole thing about people not knowing C would never be Real Programmers. And I'm glad I stayed insecure about it, or I may never have conquered what is clearly a huge deficiency in my learning patterns. OK - that's enough about me. Let's instead look at the big picture. The assumption behind the claim that groking pointers is a good guide as to whether someone has what it takes is that in order to get C you need to be able to think like the computer. But could it in fact be the case that the emphasis on C in computer science has been driving people with a strength in the humanities away from programming (in preference of people who do not have a language dominance) for the last few decades, and causing bias towards people who lack whatever it was that was sitting in my brain filtering out the correct case?
Oh - the short conclusion of my story is that - girl came over, we spent all night pretending to be a C interpreter by drawing memory diagrams on my whiteboard, she moved from likely failure to a reasonable mark in the space of four hours, was thrilled after the exam the next day, and now we both live on different continents.
I think there are leaps in what I've written here. But I'm confident I'm on to something about the relationship between people who use a certain type of thinking and have trouble with pointers. It probably doesn't apply to people with a strong background in low-level programming.
Wow, that was long.
For those with short attention span I am extracting the most important part:
"That was the lightening bolt moment. The problem with pointers in C is that the [star][varname] syntax means two different things, and they are actually contradictory from a certain perspective. In one context it means "declare a variable that is a pointer" and in the other it means "deference". Whenever my languages-brain bit looked at this it couldn't deal with it, recognised it as a contradictory positions and basically shut me down, but not in a way that let me work out what was going on. Have a look at it yourself from that perspective - hopefully you'll see what I always did. Now whenever I look at C code I instead just think "everything is an int and pointers are all about casting ints, and a star when you declare something is your way of telling the compiler that the type of this thing is int-pointer instead of just int". Thus I now have no difficulties at all."
I recognize some of my struggles in that piece. People get tired of me saying it, but I don't care:
"Chose your semantics wisely, for they do have consequences."
Ironic that in a discipline which requires such a high degree of precision in the use of its symbols and tokens consistently prescribes obscure and contradictory rules for their usage.
Although not required, success in this field is enhanced if one possesses the ability to mentally manage symbol sets with a fairly agnostic attitude towards their clarity and consistency.
When I was a newbie, I recall more than one veteran C programmer giving me a quizzical look when I asked who the hell thought it was a good idea to use the same symbol to express assignment and equality.
I had difficulty with arithmetic in school, while calculus was a breeze. I had difficulty with pointers initially while recursion was a breeze. Anyone knows any existing neuroscience that explains such anomalies. I would be glad parse it. Oh, I can write poetry but I have trouble spleing.
This will blow people's mental models of C (genuine example):
#include <stdio.h>
int main()
{
char const* c = "hello";
// this is the interesting bit - n[c] == c[n] (n = number literal) !!! I swear, try it.
printf("This program compiles and prints:'%c %c %c %c %c'\n",
0[c], 1[c], 2[c], 3[c], 4[c]);
return 0;
}
// See? I read that both forms are translated to *(c + n),
// which explains the equivalence. Weird, no?
See? I read that both forms are translated to * (c + n), which
explains the equivalence. Weird, no?
Well, that surely is evil, yet perfectly valid C code. But personally, I don't find it surprising. Of course, you need to grok what A[n] really is -- i.e., syntactic sugar for *(A + n), as you just explained.
I have always been surprised about Spolsky's observation. Although I don't know many C programmers, I can't believe pointers are something you just get or just don't.
When you learn about pointers you must also learn about addresses (star c, c, &c). If you get addresses, you get pointers. The problem you had, as it seems, was the syntactical anomaly of C where the pointer initialization is like a one-time shorthand, ie:
char star c = &a; // now, c == &a, star c == a, &c == whoknows?
I think this is a bad syntax, but that's just how it is.
Perhaps if I skipped the one sentence that mentioned how in the initialization step, the c of (star c) is really a memory address, and only thereafter is it a dereference, I would've been just as confused as you. But hence, I think the difference between getting it and not is really a syntactical understanding (like vocabulary), rather than a conceptual understanding (like grammar).
Perhaps it would be clearer for beginners to separate declaration and assignment, something like
char star c; // declare pointer
c = malloc; // obviously c and malloc are both addresses
Whatever it is though, I can't see how this piece of knowledge affects your aptitude as a programmer.
0) I've always been extremely strong in English. I tended to get high marks in it, and according to IQ tests and the SAT, my greatest strengths are in language. Pointers gave me some trouble, the * in declarations strikes me as a terrible syntax, and corners of C syntax in general often strike me as poorly chosen. Despite this, my experience does not mimic yours, either in the trouble I had with pointers, or the eventual resolution. One day, it just clicked, and I rarely made mistakes with it again. It came to me naturally after I took a break from C and picked up Python: once I was confident in the difference between a, array[a], array[a][b], etc, in both construction and accessing in python (I originally did a mental auto-squashing similar to that in Perl, despite not knowing Perl), pointers were no longer problematic. For what it's worth, I was a self-taught newbie at this point.
1) Pointers are not ints. Casting between pointers and ints is BROKEN, as they are not the same size on many platforms, including my primary one, amd64 with linux+gcc. Please, please don't advocate doing this, or thinking about it this way.
"This caused me to create a theory that I had a strong skill at certain sorts of learning based on pattern matching, and that overuse of this skill had stunted other learning skills. I told myself that I'd look out for opportunities to solve problems without using this kind of approach."
This, albeit in the inverse in your example, is a theory of dyslexia that Ronald D. Davis wrote about in The Gift of Dyslexia.
Basically, he argued dyslexics grow up with the ability to infer from juxtaposition - or intuition as I believe he calls it - and as this ability is quite powerful they use it in place of and to the detriment of more language-orientated skills.
In your case, it seems that you were gifted with more language-orientated skills from the outset. This, from what I can tell, meant you focused on the textual representation of the '*' operator instead of its context-specific functionality.
It's very laudable that you've managed to overcome your weakness through an emphasis on your cognition. It's something I have set about doing, but in the inverse as I come from the other end of the spectrum.
Thanks for detailing your experience. It was very enlightening.
The secret, as I was taught, was that "C declarations mimic usage". Or as I think of it, "C declarations are bass-ackwards".
When you say "char (* c)[4]", you're saying "this variable c, when dereferenced, will return something, when subscripted up to the value four, will return something that is a char". Or in normal speak, a pointer to an array of four characters. But when you say "char * c[4]", you're saying "this variable c, when subscripted up to the value four, will return something, when dereferenced, will return something that is a char". Or an array of four pointers to char.
Hardest thing about pointers is that it forces the programmer to switch abstraction levels. One moment you're thinking about values and the next your thinking about memory addresses and the values at those addresses. Also, I've gotten more than one :| look when I've said to people that pointers are variables of their own and you can get pointers to them and do all sorts of nasty stuff.
I have a feeling that if you learned Assembler and then a language without pointers and then came to C, you'd still be confused.
I don't think I've ever had a problem with pointers, and it may be due to the way they were first introduced to me. I read a book that said to think of * as 'value-at' and & as 'address-of'. Well, except that the * in pointer declarations (char* etc) IS different, and I just read that as 'star', so I know that its a declaration of a pointer.
Anyway, ever since then, I've always said those words in my head when reading c/c++ code, and it's always made sense. Its true I had a little assembler experience before I came to c though.
I had the same _exact_ experience with you, except, instead of letting my sense of contradiction lie dormant for a couple of years, I sat down for an intense couple of hours until I got your same lightning bolt moment. I just wanted to note this, because I found it to be an epiphany that someone else had the same exact problem with pointers as I had.
I agree this is an issue with the C syntax. I always used to write my pointer declarations as:
char* p
instead of
char p
That way there was no clash with when you are dereferencing p -
p
Admittedly this approach may not help entirely with more complex examples, like a pointer to a function that returns a point to a function that takes two pointers to void...or something. Indeed having languages that create less accidental complexity is a huge win.
The pointers itself don't seem to be the problem. Rather, it's C's strange syntax which tries to mimick a variable's use in its declaration. I.e. "int * * p;" doesn't mean "p is a pointer to a pointer to an int", but it means "* * p is an int". And so goes away any contradiction.
Yes it means both. It is just a hint how to read / write it: Don't make up some type syntax, but use the variable usage syntax and what's left is primitive types.
Ok. Now, tell me what the difference between an array and a pointer in C is, and what the syntax to pass a pointer to a multi-dimensional array to a function is (j/k, sort of).
I never had trouble with pointers because I learned Assembly before C.
Everyone who wants to understand computers should learn ASM, preferably with something nicer than x86 GNU Assembler (%eax, come on, how about "R1"). And not that pseudo-Pascal "High-Level Assembly" nonsense that hides the details from you so you don't understand what's going on.
If you learn hardware first, pointers are pretty easy. However, learning hardware inevitably slants your thinking towards solving ALL problems in those terms, which is bad for higher level architecture and design thought processes. When all you have is a hammer...
It's not about stress. It's about juggling a bunch of delicate state, and the state is being held in short-term memory registers that are easily clobbered.