Three Star Programmer

A rating system for C-programmers. The more indirect your pointers are (i.e. the more "*" before your variables), the higher your reputation will be. No-star C-programmers are virtually non-existent, as virtually all non-trivial programs require use of pointers. Most are one-star programmers. In the old times (well, I'm young, so these look like old times to me at least), one would occasionally find a piece of code done by a three-star programmer and shiver with awe.

Some people even claimed they'd seen three-star code with function pointers involved, on more than one level of indirection. Sounded as real as UFOs to me.

Just to be clear: Being called a ThreeStarProgrammer is usually not a compliment.

No-star programming is becoming trendy again, because of C++ and templates. The aim here is to avoid indirection completely, inline everything and have it all evaluated at compile-time. None of that "dynamic" nonsense. -- DaveHarris

It's gone too far the other way, I think. I've been attempting to recruit C++ programmers. In a dozen interviews, all but one couldn't handle simple pointer manipulation (reverse an integer array in place). The one exception struggled, but got there eventually. I know C++ doesn't need pointers as much as C, but I find it worrying that such people claim C++ competence, and can apparently find jobs programming C++. -- PaulHudson

See FreshmansFirstLanguage.

See TheDumbingDownOfProgramming and SkewedBellCurve

In Smalltalk: anArray reverse. In C++ Standard Library: aList.reverse();

Of course (actually, not of course, since that isn't in place). But that's not the point. I don't want to know if they can reverse an array. I want to know that they understand how C++ and pointers work enough to be able to implement things like "reverse an integer in place". -- PaulHudson

Pardon me for butting in here, but isn't that precisely the point?

I want developers on my team who think about what they are trying to accomplish. One of us has misunderstood the irony of this page. I find myself (here in Cambridge, MA) surrounded by an army of C++ (and now Java) "developers" who salivate at the prospect of showing how much they know about "how C++ and pointers work" -- and who don't have the foggiest idea of why anyone would ever want to reverse an array, never mind in place.

In a perfect world, I would like to not have to make a choice: I would expect every seasoned professional to know how to accomplish such feats. But if I have to choose, I'll take the abstractor any day. The failures and shortcomings I do battle with almost always result from confusion about *what* problem needed to be solved. The pointer expressions almost always work precisely as intended by the developer. --TomStambaugh

I don't think that's the point, no. I understand the problems ThreeStarProgrammers cause. But people incapable of being OneStarProgrammers are problematic, too.

Remember I was describing an interview. I agree that if they should find themselves writing a program and needing to reverse an array, I'd rather they used a library function than wrote it themselves.

But in our situation, we need programmers who can write code at a similar level to this - reversing an array is just an example for discussion at interview.

I don't want programmers who know only how to fiddle bits, but not why. I don't want programmers who can only handle connecting previously written functionality, but can't get down to the low level stuff when they need to.

I'd probably take the good abstractor, too. But I haven't had many good abstractors turn up, either. I've been very disappointed with the level of skill of the people I've been seeing.

And in my experience from these interviews (and similar interviews in past lives), the pointer expressions do not work precisely as intended by the developer (I go on in my interviews to ask the candidate how they would satisfy themselves that the code they've produced worked. Most of the code produced fails on boundary cases, and they have no idea how to test it anyway. They don't think about things like boundary cases when producing the code, either. Depressing). -- PaulHudson

Hmmm. We seem to share a desire for programmers who can do both. When assembling teams, I try to find and hire PragmaticProgrammer's, who perhaps strike the balance we each seek. My experience has been that this is more an attitude than a particular skill set.

I've discovered that I find it much easier to teach a naive abstractor how to program than to teach a naive programmer how to abstract. I can (and have) helped a 10 year old learn how to dereference pointers. I cannot yet help (very much) even very accomplished programmers (of any age) learn to discover the "aha" feeling of a good abstraction. My experience has been that the self-professed ThreeStarProgrammers are the hardest to reach.

The ThreeStarProgrammers I've known have been devastating to my teams and the code they produce. The attitude that, in my experience, they seem to bring with them is "I'm smarter than you and tougher than you, and I'm going to prove it in my code. Watch *THIS*!" ... and out pops some inscrutable hieroglyphic mess. I cite the C++ reference manual as the canonical example. This attitude has, in my experience, been so destructive that it has totally overwhelmed any advantage brought by their technical expertise.

Conversely, the teams of more humble craftspeople I've worked with have been compellingly more productive. I find it a joy to hear "I'm not really that great a programmer, I was a History major -- but let me show you the way that we combined Visitor and Strategy in our last project, I really liked how it came out" ... and onto my whiteboard flows an elegant, terse work of beauty. I respond to the humility, to the shift from "I" to "we", and to the obvious appreciation for the abstraction itself. Yes, these people are hard to find: that's why I insist on paying them well, and hiring them when I meet them, whether or not I have open req.

While I appreciate the particular skill required to emit this code, I find it most useful as a counter-example. The developers I want to hire are the ones who look at it and say "Yuchh! It might work, but why on EARTH would somebody write it THAT way!". I want the developers whose sense of taste and hunger for abstraction compel them to complain about code like this. --TomStambaugh

OK, I think we're violently agreeing here. One of the best programmers (as in useful to the organization) I've worked with recently was a convert from Human Resources, and fits the pattern you describe. So we want people who could be ThreeStarProgrammers but have the good taste and judgement not to want to be. -- PaulHudson

Yup, that does it for me. -- TomStambaugh

It is like the definition of a good pilot: one who uses his/her superior judgement to stay out of situations in which he will need his/her superior skill. -- StefanVorkoetter

As a minor contribution to this debate, I tried to get clever in implementing the in-place reversal and happened to produce at least three incorrect versions, all failing on boundary cases as a result of using "!=" rather than "<" (don't ask). My only redeeming excuse is that I caught all of them before I even thought about devising tests... -- JoostMeerten

How do we distinguish between terse idiomatic code and the incomprehensible? What level of expertise do we write to? I try to to avoid GuruOnlyCode but I've certainly strayed across that line myself at times (even though I'm not a guru).
 A HumbleProgrammer

Its like distinguishing art from pornography, its hard to define, but I know it when I see it. *smiling* -- TomStambaugh

  : It's easy to define.  It's only pornography if I don't like it.  It's PropellerBeanie code if you enjoyed writing it too much.

It's been a while now, but I once worked one a program that had at least 4 levels of indirection at one point.

The program was a screenscraper. Screen data was sent from the server as a series of bytes, which the original author simply dumped into a buffer of memory. He would then proceed to walk over parts of it, casting snippets of it into the particular type that he wanted at a given point.

The buffer was a structured blob, and some of the data was offsets to other parts (it was an index of fields). The field data, in turn, had offsets to other parts of the data, to handle things like string literals, formatting code, and so on.

In order to get the screen title (which was just a special field in the screen data), the original author managed to do four or five pointer dereferences within the single statement, including casts and offset adjustments. The statement was spread over four or five lines, at 80 characters to the line (and no indentation... with indentation it grew to about 8 lines).

Still, this was easier than another part, where he treated the snippets of data as arrays. Of course, each reference used a different data type for the array, and thus different sizes. And here I was trying to follow what the program was doing, given a printout of the buffer and a lot of highlighters... (he had a FencePostError in one of his loops which was causing the formatting data to be read in incorrectly). -- RobertWatkins

One problem with the notion of ThreeStarProgrammers is that the term only makes sense in the context of C/C++, where * is the pointer dereference operator. In other languages with explicit pointer frobbing, replacement of * with the appropriate lexeme is straightforward--you could have ThreeCircumflexProgrammers in Pascal, for instance. How about ThreeCaratProgrammers?

Obviously follow Algol-68 (the mother of all languages) and call such a programmer a ref ref ref programmer NickKeighley

But what about other languages, where there are no (explicit) pointers to dereference? How should we verbally abuse programmers in other languages (LispLanguage, SmalltalkLanguage, PythonLanguage, JavaLanguage, PerlLanguage, etc.) who are far too clever for their own good? :) Anyone got any good suggestions?

Well, one might argue that anyone programming in LispLanguage is already too clever for their own good. ;-)

Also, what about nested #if,#else,#endif's? Those can be even harder than pointer dereferencing, especially considering cross-platform, cross-compiler, even cross-language issues.
I'm inclined to called this whole thread PriestlyProgrammers? or how to write arcane code that only the initiates can be expected to understand and them only on alternate Tuesdays. Good for job security, rather poor for maintenance. --RaySchneider

Dev! Null! Seize the heretic who profranes our rel...craft!

I had something akin to a three-star moment the other day, in PythonLanguage of all things. For some reason I ended up needing a function that takes a function that returns a predicate, and returns a new function that will return a new predicate, being the complement of the original predicate. It seemed like an elegant solution at the time... --someone

Nothing wrong with using a HigherOrderFunction --MontanaRowe

Q: What do you call a person who knows where to find a ThreeStarProgrammer?

A: A ThreeCruitor?.

Q: What do you call a company that needs or uses a ThreeStarProgrammer?

A: ThreeTarded?

The underlying issue here is probably one of not making a new object when one is required. If someone's using three levels of indirection, they probably should have made some sort of a container class instead, with some suitable methods. The three star constructs are then converted into a named object type with some named methods for using it. A big improvement.

Three star programmers fail to see how a new object could help, and the three stars happen because some other class is now doing too much.

Remember it's a rating scheme for C programmers. No classes

Note that coding classes in C is a standard CeeIdiom?

it's been a while, please forgive my syntax errors. Here's code for a list insertion that does not need special handling for the empty list, or matching the end of the list.

 struct foo { struct foo *next; int thedata; };

struct foo *thehead = NULL;

insert(struct foo *bar) { foo **ptr = &thehead; while(*ptr && performSomeComparisonCheck(*ptr, bar)) { ptr = &(*ptr)->next; }

bar->next = *ptr;

That's ugly code, all right. A global variable to hold the array, and a wholly unnecessary level of indirection just to avoid a test for end-of-list (though what does performSomeComparisonCheck() do? But still, only two stars.

A valiant effort, though. Do you work for MicroSoft? --someone

If you can do a find and replace for a variable with a * before it and, afterwards, can't find the variable without the *, you didn't need that * at all. *ptr is the data you care about; you never use ptr, so replace all *ptr with ptr (once and only once), and you get the code you wanted. --MontanaRowe

This is my code (been a while since I looked at this page). The global variable was merely - heck - you have to have a variable somewhere where you are writing an example. performSomeComparisonCheck returns a boolean that is used to determine which element we want to insert in front of. It's metasyntactic - would you prefer that the function take a pointer to function? This is C circa 1986, remember, not C++. And the comments about removing all the *ptr is flat out wrong - I'd like to see him try. As for the code being ugly, I rather like it. It's no more difficult or involuted than doing loops via recursion in LispLanguage. -- PaulMurray

I was wrong on two counts. First, my pointer statement only holds for if your pointers are the same type as the thing to which you are pointing. Second, the precondition is not met. I said, "you never use ptr," which is false: ptr = &(*ptr)->next; Thirdly, and not a "wrong" per se, my wording was really confusing. --MontanaRowe

The above statement is wrong because it completely disregards the fact of passing arguments by reference using pointers in C which does not support references directly like C++

Hence a function :

void func(int *n) {
  *n = 10;

I've been doing some work that requires reading the python interpreter's source. It uses three-stars in several areas. I think its used as a pointer-to-the-first-element-of-an-array-of-pointers-to-dynamically-allocated-objects. How's that for abstraction? Anyways, the irony is beautiful: one of the languages with the best syntax is implemented with this crap... haha AJB

Three-star code naturally comes up when you need to use an ordered index an array of pointers to strings (in that we ask for a position in the array, not for the string itself). The strings are of type char const *. The data block is of type char const *const *const [02]. The index block is of type char const *const *const *const [02]. (The innermost indirection does not count because the string type can be made oblique; however, the outermost array makes up for this deficiency.)

One can easily avoid three-star programming by the judicious use of typedefs:

 typedef foo *foo_handle;
 void andWithOneLeapHeWasFree(foo_handle **bar);

I'd rather have three-star programming than an overabundance of typedefs. Especially typedefs of pointers. Ugh. -- wrl

I have seen in production code (though not written myself) four stars for a configuration structure (in nginx). It is beyond my current understanding why this uses four stars, but I did at some point understand its use.

Source: nginx/src/core/ngx_cycle.h (as early as 0.1.11 and as late as version 1.5.9)

May be a ProgrammerStereotype

See also: ThreeStarPerl, ThreeStarAssembler, ThreeStarJava, ThreeStarSwap, YouMightBeaThreeStarProgrammer, and ThreeStarProgrammerExamples


View edit of September 17, 2014 or FindPage with title or text search