Deep C
slideshare.net
slideshare.net
I think the boy's initial "I would never write code like this" response is spot on. I also agree with the boy's implied annoyance and "who cares what this badly written program actually does" stance.
These are fairly terrible interview questions. They test knowledge of standards and various obscure behaviour, but it's far more important what kind of code (readable, explicit, maintainable, simple, architected, commented, performant, bug-free, etc.) the person writes. In real life, the boy may actually be a better programmer than the girl. It's just not a good, comprehensive test.
Nevertheless the examples are fun, but I would never ask them in an interview. Interview time is precious and not to be wasted on stuff like this, unless you're interviewing for a compiler job...
EDIT: Most of the above applies to the first, C part of the interview. In the second, C++ part, the stuff the girl says is mostly basic knowledge that is useful on a daily basis.
If I was interviewing, I'd much rather hire someone who said "I'd never write code like that" to someone who reels off what might happen in every possible scenario, unless I was hiring for a compiler writer.
The man in the example might know to "never write code like" a variable incrementing and assigning to itself again, but there might be other, more subtle pitfalls in design and implementation that he succumbs to because he hasn't given much thought to all the different ways that the rules of the language can interact.
Note that it's not all stuff that you can just look up: although you can look it up, it might not be obvious that it needs to be looked up. I had no idea there was a difference between `int f()` and `int f (void)`, and if that ever caused problems for me I doubt I would have been able to track it down easily.
"A gentleman is someone who knows how to play the bagpipes, and doesn't."
Understanding the memory model and execution model is a big plus, because it lets you write correct and efficient code (and if you are using C you care about correctness and efficiency). Recalling arcana about the C99 standard is just overkill and asking someone to pull stuff like that out of a hat in an interview is asking too much IMO.
sizeof-test.c: In function ‘main’: sizeof-test.c:5:3: warning: format ‘%d’ expects type ‘int’, but argument 2 has type ‘long unsigned int’
Compilers have warnings so that you don't have to remember archaic bits of C. :)
The parenthesis you usually see after it are part of the argument, which for type names looks like a cast to the type in question. Like any other cast, the syntax is the type name in parenthesis.
Apologies if you already knew this.
Suggestion: enable -Wall and treat warnings as errors.
gcc -v shows that it's been configured with --disable-werror
It's only a net win if the price is right and the project requires such knowledge (in the case of C, mostly likely the trade-off would be a bad one, experience at that level comes at a steep price, fixing bugs due to a lack of knowledge about what happens 'under the hood' is expensive too...).
An example: I have been taught to never attempt shifting the gear in a car before pressing the clutch. I accept this as a dogma, but I have no idea what would have happened had I tried really hard. When I asked "why", the only answer I got was roughly "the engine would break". Only some car mechanics / engineers have enough understanding to explain in detail what would have happened. They have knowledge, I'm following a dogma.
If you time it right the shift will happen from say to 2nd to neutral at one rpm (to neutral is always possible without the clutch), then you reduce the rpm until you know it will match 3rd gear for that speed (good hearing helps here) and then you can make the shift from neutral to 3rd without any force at all. The more 'sloppy' a gearbox the easier this goes.
As long as you don't force things you'll be fine, if you do force it you'll likely be greeted by expensive grinding noises.
On the classic 'beetle' and 'mini' engines I can do this without fail or any chance of damage.
Modern cars (read more expensive) I haven't tried.
Of course this is not what you're supposed to do but it's interesting when you have a 'junker' to play around with it until you can do it right.
Your transmission is a series of gears with varying ratios of wheel revolutions to engine revolutions. When you upshift, your engine requires fewer revolutions to drive your wheels. When the no-clutch-shifter lets off the gas, the engine's RPM begin to fall. With the correct timing, the shift is performed during this drop in RPM such that the engine's current RPM matches the RPM necessary to drive the car at this speed in this new gear. This obviates the need for a clutch, since the engine and the rest of the drivetrain (for which the transmission is the link) are being manually synchronized: the shift is performed with a technique that keeps everything spinning at the same relative speed.
When you depress the clutch pedal, the transmission disengages from the engine, allowing you to freely shift without regard to the engine's machinations. When you let out the clutch, what typically occurs is that a carbon disk slips a bit before fully reëngaging, allowing the engine and the rest of the drivetrain to gradually synchronize. This is the "catch" that you feel when you let out the clutch.
To illustrate explicitly, let's say we're shifting from 3rd gear to 4th gear at about 30 MPH, and for this car at 30 MPH the engine must spin at 5000 RPM in 3rd gear and 3000 RPM in 4th. Without the clutch, you left off the gas and pull the car out of gear (taking it out of gear is a split-second after letting off the gas), the engine's speed (RPM) begins to fall as you pull the shifter into 4th, and just as the engine hits 3000 RPM (on its way down to 1000, the idling rate), you shift the transmission fully into 4th gear. With the clutch, you jam the clutch and shift, and the engine could be anywhere from 1000-4500 RPM when you let the clutch back out. The clutch slips a bit, allowing the engine to reach the appropriate 3000 RPM before it is fully engaged. Without the clutch, you have very little room for error.
For an embedded systems C coder (which was the stated context of the interview) working in a memory and CPU-constrained environment, seemingly arcane knowledge of the compiler and runtime can be crucial, so in context I think these questions were very appropriate.
I would say rather than exclude low-level exercises like this, in favor of more high-level concerns like GOF, SOLID, etc., why not use both types in order to find out what kind of strengths the coder is bringing to the table?
I've found that a lot of coders who tend to be very design/architecture/strategic focused (for instance, me!) tend to have weak areas in low-level/tactical concerns, and visa-versa. Both kinds of coders can be a great asset to a team, and provide a diversity of expertise while pushing each other to grow in weaker areas.
If you can find a coder that does both strategic and tactical very well, you might have found a team lead.
I think in real life the boy will shoot himself in the leg without knowing the order of evaluation in C.
void foo(void)
{
int a = 41;
a = a++;
printf("%d\n", a);
}
She says 'a gets an undefined value' but that's not true at all. There are compilers that will look at that undefined function and omit the entire body, including the print.It is extremely important to understand that undefined behavior can and will break everything, even if it appears to be behaving whenever you look.
For the first half, the boy is making all the right decisions. He's not taking changes and using little compiler tricks to do things. He's not being clever. (For the second half, they appear to deliberately make him stupid. I'm not sure why.)
She, on the other hand, has a lot more tools in her toolbox. At the very least, she'll be tempted to use them. And if anyone else is working on the code, they'd better know those tools as well as she does. That's a dangerous situation.
Of course, it depends on what they're working on. If they're working with Carmack on his new 3D engine, she's the obvious choice. He would never keep up to Carmack.
Well heaven help you if your application gets more complicated and you need to figure out something more difficult.
A truly good programmer knows all the tricks and knows when to not use them.
If all pay were equal, yes, I'd want her, with the ability to keep her code simple and never be tempted to use complicated things where they don't belong. I'd also want her to understand everything down to the logic gates, and be able to write her own operating system and VM from scratch. If she could also make the perfect waffle, that would be desirable, too.
But pay isn't equal, and I'd be a fool to pay for more than I really need. (Assuming I'm paying fairly.) If the project gets more complicated, I'll educate or hire accordingly. In fact, by that time, he may have educated himself up to the level I need.
Barring any serious deficiencies, she'd be a steal at 2x pay.
> If she could also make the perfect waffle, that would be desirable, too.
is the sort of casual sexism that I'd love to see less of.
tl;dr: Nerdy guys don't have much in life. Let them have C.
So I don't mind a bit that they chose the roles as they did.
But that's because there were only a handful of them of them, and if you want to make it as a woman in CS, you have to want it bad, or you won't put up with being the one woman in a class of 20.
It doesn't necessarily follow that you will use that knowledge to be "clever". It helps you avoid pitfalls and debug strange behavior more easily. It helps you know when to use them, and when NOT to use them.
Someone who only knows how to program in practice is ignorant, and someone who only knows the "deep stuff" and can't program effectively and cleanly is just a "language lawyer".
I don't really know C that well -- certainly nothing spec- or compiler-specific -- but I got "deep" answers for most of the questions just because I know that generally C compiles straight to machine instructions, and I know how computers work. (I got 5/5 on the little quiz, and the tutorial on sequence points was completely new to me.)
C isn't hard. Thinking like a computer, in any language, is hard. The ability to predict what code will do, while important, is less crucial than the ability to figure out what's really going on when you get one wrong. I feel like calling that ability "deep knowledge" is putting the cart before the horse.
To put it another way, "I don't know the answer to that question, but here's a guess" and "I know the answer to that question, here it is" are both less impressive answers than "I don't know the answer, but give me a second and I'll figure it out for you."
Or maybe I'm missing the point.
I hate slideshare requiring registration or facebook login before it allows me to download it in some decent format.
I hate slideshare so much.
Maybe slideshare has some sort of architectural problem? Is it seeking through the slides linearly for every request? Maybe slides after a certain point get read from disk in multiple segments? Maybe at that exact point in time, the load on the server increased?
Who knows :(
At least for the first printf example, I would employ the boy rather than the girl. The boy gave a correct answer - what was missing, what would happen. The girl spouted a bunch of standards that added no value (with the exception of the 'return 3' which does indeed demonstrate deep knowledge).
In particular, the 'you should add a blank line at the end of your code' comment would immediately make me suspect her to be a finicky letter-of-the-law PITA who would be a nightmare to work with.
Standards are all well and good, but if your code compiles with no warnings and runs without errors according to the spec you were given, nobody of any consequence is going to give a damn.
Not all of it. People who do not understand the rule of three or when to declare methods or deconstructors virtual can surely cause headaches.
Wait, what?! The whole point of this slideshow is that without a deeper understanding of your language you can end up with errors that you don't anticipate or understand.
Your code compiles and runs today. But then you rebuild on a different machine, or with different compiler optimization level, add a seemingly innocuous piece of code here or there and then it starts acting differently.
Or you deploy it to your customer and they start seeing intermittent problems with it because you failed to understand when to initialize auto variables.
You can never have too much knowledge about the language (and tools) that you are using to build your code. I would even go further and say the same thing about the hardware you or your users are running your code on.
Your code compiles and runs today. But then you rebuild on a different machine, or with different compiler optimization level, add a seemingly innocuous piece of code here or there and then it starts acting differently.
IMO this is the problem with the deck. The "problematic" code is code that ppl who don't know the corners of C/C++ aren't likely to write. If you're tempting sequence points in your code, you're asking for trouble, even if you know the standard well because now you're tempting fate you don't make a mistake and you're assuming every compiler implements the standard perfectly (and we all know this isn't the case).
And the smart girl makes at least one potential error. She says that, "You said that your runtime is 64bit so that means your pointers are probably 8 bytes".
First he never says the runtime is 64bit. He says its a 64bit system running in 32bit compatibility mode. What does 32bit compatibility mode mean? Potentially a lot of things. For example, on Windows it means you have 4 byte pointers. Rather than conjecturing -- this is something she could have just said, "How long are your pointers?" But her character was one that had to have an answer -- even if it meant making a potentially wrong assumption.
What everyone is missing is how interesting the tutorial is, despite it being about a really dreary topic. I'm reading a book called "Why Don't Students Like School", which goes into some detail about this kind of thing. It's quite good (and really should be, given that it's about how to make education interesting).
"What you learn today Johnny?"
"Pure sodium goes BOOOM!"
"Why's that?"
"Um, the teacher said something about chemistry, but we were all just watching the fireworks."
We're interviewing some candidates for embedded C programming right now and these are the types of questions (although generally easier) I ask to find out the level of C expertise. For embedded systems with a focus on stability I want candidates who have a very good grasp of the fundamentals like the difference between normal globals, statics, volatile, etc. Also I'd like people who understand enough about the linker to know about data segments, zero init and stuff like that. For embedded you generally have to be able to implement interrupt handlers so the exact translation of a line of C code to assembly is important, as is knowing what's atomic and what isn't.
I'm continually surprised how many programmers have worked on embedded systems in C without being able to answer almost any of my questions. Many seem exactly like the guy in the examples, none like the girl.
Edit: I only went through the first twenty or so slides. After disagreeing with the initial premise I didn't think the author earned any more of my time.
The first is to remedy the misconception that "C is a really simple language". It's not.
The second is because while you'll never write any code like this, you'll probably have to debug code written by someone who did. That means that in order to use your time efficiently, you need to know what matters and what doesn't. Writing a regex to replace all "static int foo" with "static int foo = 0" is going to waste your time and not expose the cause of the bug. If you didn't know that, your time would be wasted.
I much prefer C over C++ because, at least in comparison, C is a much simpler language.
However, the motivation for Google's Go is suddenly much clearer...
(OK, if you don't want to buy the book, here are the directions: http://books.google.com/books?id=4vm2xK3yn34C&pg=PA347...)
and i just found this - http://publications.gbdirect.co.uk/c_book/ - which looks like a good basic intro.
Not if by "deep understanding" he means "knowing trivia about underspecified corners of the language that have nothing to do with getting the job done".
static int a;
++a;
I don't care what the standards say, please just type the initialization value you need. It's inconsistent to pick a special case, 0, out of 4,294,967,296 possible values, and type something different in the special case.Another dangerous thing in C: assuming the size of integer types.
On the other hand, there are very important pragmatic reasons for using 0 to initialize data; the OS loader can give you zero pages (corresponding to bss segment or equivalent) for "free"; free to the point that they don't need to be stored in the image, and allocated lazily as needed.
Safe initialization of statics is both possible and fast, thanks to an algorithm by Mike Burrows:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n266...
It is also guaranteed in C++11. GCC has been implementing thread-safe initialization since version 4, I believe, but I'm not sure whether they use this algorithm.
I personally think it's better, where possible, to define the relevant types such that a zeroed structure as a valid state, or else use trivial initialization (where "trivial" might be defined as literals or constant expressions). Where this is not possible, explicit initialization (e.g. called directly or indirectly from main()) ought to be used.
You are talking about global static variables, and yes, that's a tricky matter.
It's like encouraging people to speak in baby talk because the people listening shouldn't have to understand all the detailed minutiae of the English language.
And if you don't understand the language, then why, oh why are you trying to read or write it?
a) To get things done.b) To attempt to understand the language.
Not everyone can learn the complete work of C standards and quirks just by reading, some need to read and write it.
I think the case for omitting "= 0" is weak at best. If you encourage this style some novice/intermediate programmer could do the same thing for a stack variable, which will bite him/her. Given all these costs/downsides, what is the benefit of not typing those three characters?
As the gcc man page says, "These warnings are possible only in optimizing compilation, because they require data flow information that is computed only when optimizing." (emphasis mine)
Also, you can use -Wuninitialized without optimisations and it picks up the problem, at least in my version of gcc (4.5.2).
Recent versions of GCC have a revamped syntax tree analyzer, which was needed for the new tree-vectorize features. It does more work from the outset these days, so I'm not surprised if it throws the warning earlier.
That note came from the man page for i686-apple-darwin11-llvm-gcc-4.2, FWIW.
Q: "What does this buggy program print."
A: (hits first person with a stick)
"Right!"
That correctness has varying levels of importance depending on the industry/application, but for a sufficiently large and complex project, the benefits to writing correct code is extremely valuable.
For instance, if it were a team environment and I had to write some functionality using the code supplied by the girl, I could simply assume standard conventions and immediately attempt to use her classes in my code. If written correctly, that code might work without further effort my on end.
If the code were supplied by the guy, I would likely have to go in to the implementation and ensure that the behavior conformed my expectations. And having to go through and read the code could require a significant time investment.
And of course, reading code often takes significantly (10x++) more time than writing it, so minimizing the necessity for it is essential in maximizing productivity. I would suspect that in most programming teams that reading other people's code is what eats up the most time, yet this time can be significantly reduced by following such a standard.
When the slideshow begins to refer to C++ conventions and the "rule of three," the guy shows that he doesn't take into account that others may need to work with his code. This can be dangerous as this single new team member can actually cause the total productivity of the team to decrease.
While this guy is just a made-up character, I wouldn't be surprised if most programmers think/act like him. At the very least, the majority of the ones that I've worked with, do.
On another note, the minutia details of C do come up if you work with and read enough source code. Being able to understand it is the more important issue at hand. Also, in response to many comments stating that writing code that works based on such minutia is poor practice, I think the girl would likely agree.
1) Add "-Wall -Wextra -Weffc++" and fix the problems found 2) Have some insights into the various C/C99/C++ language contracts, including what sequence points are, how data is packed, and how expressions are evaluated. 3) Learn when to use delete or delete[], when to declare a destructor virtual.
If anything in this summary piques your interest, you may want to check out the full presentation.
Or, if you prefer: It is a crazy girlfriend: serving your whim one second, burning your clothes the next. Pay it close attention or be ready to buy a new wardrobe regularly.
I appreciate the presentation style too. The characters clearly represent the difference between deep understanding and casual knowledge of the subject. Good stuff.
The key to me is how the candidates gain the understanding of whatever languages they use. Meaning, is programming something that they really enjoy and are learning on their own time and solving some of their own problems. Or, are they simply picking up programming to try to get a better paying career (aka the types of people that are often drawn to those 6 week learn to program type of schools). Therefore, for me and the interviews I have done in the past asking candidates about books they've read or programs they've worked on (for themselves or others outside of their employment) are often times much more enlightening.
I don't really care if <stdio.h> has a printf definition, or whether C99 behaves differently. You might care more if you are an app programmer.
What I care about is the universal properties of the language that applies everywhere. Also that's where the strength of C comes out. For example, what is a function? What does it compile into? Why are there arguments, return values? How do they get represented? These are far more important questions than how C99 dictates entering the program from int main(). That's least of my worries. Programs I write usually enter a C program from _start
ps you say standards (plural) - all the above is c only.
You must be a language lawyer in order to do anything moderately complex and then be met with "the standard doesn't say anything about that!" when you realized the standard library doesn't include the most basic of things (yeah comp.lang.c, I'm looking at you...).
Yeah, I know. C++0x will be the grand unifying standard to rule them all...sure.
When I write out a file containing "aaaaa" with nano (to avoid any unusual tricks vim might use), and then hexdump it, I get "6161 6161 0a61". In other words, nano slapped on an ending newline when I saved. It is my impression that everything does this.
Edit: Apparently nano has a -L option that skips the last-line normalization.
It was also neat to see my code run flawlessly under Valgrind (we were attempting to debug an issue with the 32 bit Intel Solaris build---it may be a compiler issue since that's the only platform (out of the ones listed above) the code broke on, but I haven't investigated the issue enough to say for sure).
Q: So why doesn't the compiler reorder the members in the structure to optimize memory usage, and execution speed?
A: Some languages actually do that, but C and C++ don't.
And that's because...?
struct list_node {
struct list_node * next;
struct list_node * prev;
}
struct useful {
/* This struct should be part of a list, so include the list header first */
struct list_node;
/* That data that this struct is storing follows */
in_addr_t sip;
in_port_t sport;
in_addr_t dip;
in_addr_t dport;
}
Then, you can write a generic list walker by casting the a "struct useful * " to a "struct list_node * " since the first fields will match up.This is just one example, another would be parsing and unwrapping ip headers and data sections. As I'm sure you've noticed, C prefers explicit to implicit, and this applies to the layout of memory as well.
edit: example: http://stackoverflow.com/questions/3766229/casting-one-struc...
Can you point me at the relevant paragraph?
This is from the draft N1256 document as I don't have access to the official standard. From 6.2.5 - Types:
"A structure type describes a sequentially allocated nonempty set of member objects (and, in certain circumstances, an incomplete array), each of which has an optionally specified name and possibly distinct type"
I can say that a lot of device drivers in the Linux kernel depend on this ...
As for Linux device drivers... it wouldn't be the first time that the Linux kernel (and especially device drivers) assumed a certain compiler behaviour which wasn't guaranteed, with hilarious results on later compilers.
That said, relying on obscure stuff is where the problem lies. Long time C hackers almost always agree on one thing (if maintainable code is their goal and they're not engaging in some silly contest on how to squeeze a certain program into a ridiculous number of bytes or on how to write a program that you can't understand on purpuse): If you want your code to be maintainable in the long run try to be as transparent as possible about what the code does, don't rely on obscure or implementation dependent features.
So the 'girl' probably has huge experience, but the 'boy's answer is equally valid from a novice's point of view, because it (probably) gets the job done with a minimum of fuss (at least wrt to the initial set of questions). At some point he'll have to expand his knowledge.
If you had to sit through an exposition like that for every silly simple question the days wouldn't be long enough to get any work done. Literal answers like the girl gives are rarely productive, but it is good to have the knowledge that allows you to give those answers. It isn't a must for every position though, and even 'lesser' programmers have to learn somewhere (but when you're hiring you want to hire the best you can at the right price).
Oh, and please, do initialize your local variables before first use ;) Even if you are compiling with the debug options on, not initializing your locals before first use is a really bad idea, no matter how much you know about your compiler.
Later on in the series the differences get more interesting, keep clicking :)
What is probably frightening off many people from C is that there is a lot of arcane knowledge that the 'girl' is exposing that can actually be helpful during the debugging stage of writing a program, and that there isn't really a manual that you can read that will tell you all that stuff.
The only way that I know to come by it is to write code for many years and to run into those issues. I've had a couple of all-night debugging sessions that are still pretty vivid after many years and it is interesting that those bugs taught me lots about how compilers optimize, possibly more than I learned from reading books on the subject.
C bugs can be pretty subtle. The 'girls' knowledge can be helpful while doing hardcore stuff.
Given the choice between the two candidates I'd probably pick the girl (assuming she wants the same salary, which is very unlikely) and put a daily time limit on exposition ;), the 'boy' simply doesn't have that much experience yet.
Oh, and you can declare your 'main' fuction to be int main(void) all you want, argc and argv and envp will still be passed to it.
Keeping your globals 'static' in the file they are declared in (instead of (gasp!) having a bunch of globals that are declared 'extern', try to avoid that if you can) is good practice.
And so on. In short, there is a lot to know about any programming language, you can use questions like these to gage experience levels, don't rely on obscure stuff, explicit is better than implicit, don't expose more than you have to and so on.
where can I find more discussions like that?
2) I hate C++.
3) Oh, crap, I already knew 98,5% of all this (thanks to the C++ FQA in part, but still). I guess I'm a language lawyer now…
Making the reader feel dumb makes for a terrible presentation choice.