6 ms·
Deconstructing K&R C (2010)
- michaelfeathers 14y agoZed is dinging examples in K&R for incorrectness, but he's inadvertently deconstructing the notion of correctness. It's more contextual than most developers want to believe.
- p4bl0 14y agoAgreed. And it starts from his very first example, about the implementation of the copy() function. C is not a type safe language and programmers who use it knows that, sometimes it might even be an advantage. For instance for me, if I'm writing or using such a copy() function which does not ask for a length to copy, I know it is because it will need a proper C string. For some others it may be that they know that their functions will use a global maximum length and that the inputs they provide to the function are respecting that. But no half-decent C programmer will use this function as a totally safe one on any possible inputs. Especially if they saw the implementation. The "correctness" the author is asking for here is not what you want from a typical C functions. If you really need this kind of "correctness" then maybe you are using the wrong language and should check our either a high-level tolerant scripting language or a statically typed one.
- michaelfeathers 14y agoYes, and that is just one level. When you write about programming you have to be very clear about what you are trying to get across, and when you put that focus into practice other things suffer because you are competing against the rest of the world for the reader's attention. Kernighan and Ritchie could have decided to write the book as absolute hard-asses, making the most bullet-proof copy routine imaginable, but in the end they would've been writing a different book, not a primer for a language.
- Tloewald 14y agoIf you're illustrating concepts — such as iterating through an array to find a specific value which will be there because the data structure being examined is defined to contain it at some point, then writing a function that assumes the value won't be there is didactically bizarre. Zed is suggesting that the teaching point of the code sample be ignored in favor of an altogether different and less useful teaching point. This is not a case of not understanding C (which he cleary does understand) but not understanding how to teach. At best, his entire point could be addressed in K&R by a cautionary footnote or a later discussion of code hardening.
- eliben 14y ago// use heap memory as many modern systems do char *line = malloc(MAXLINE); char *longest = malloc(MAXLINE); assert(line != NULL && longest != NULL && "memory error"); // initialize it but make a classic "off by one" error for(i = 0; i < MAXLINE; i++) { line[i] = 'a'; } So, you create something that does not fulfill the C library invariant of what constitutes a "string", and then pass it to a copy function that assumes this invariant? It isn't a fair thing to do, and frankly, I doubt it many beginner programmers care about things like this. Yes, they may run into such a "defect" and be very miserable for a while, but that will just teach them about debugging, and most important, invariants. Zed, I appreciate your work, but if this is the direction you'll be taking with these articles, then don't bother.
- chj 14y agoYou are being too kind. I was expecting some earth shaking discoveries, only to find the author doesn't understand C.
- PommeDeTerre 14y agoZed and his writings are not to be taken too seriously.
- to3m 14y agoI suspect this one is a diversionary tactic. I guess Zed Shaw suffers from nerd burnout. As in, a sort of more emotional burnout from having had to deal with them all the time in the past - or at least, that's what I get from some of his writings anyway. So I imagine him popping this stuff in as a sort of early warning system. It's all true enough to be right, and true enough to get his point across, BUT IT'S NOT TRUE ENOUGH FOR A NERD. So any time somebody complains about his strcpy example, or 0-terminated C strings, or whatever, that's his nerd alert. This person is not worth dealing with, and now he can block them, or set up a mail filter to put their email in the junk folder, or whatever, without having had to invest any time in finding this out the long way. There was also a bit in one of his essays about the way ruby fans were always these stupid armchair pop psychologists.
- Camillo 14y agoThere is no reason to assume that the correct length parameters would be passed to safercopy. Indeed, there are many buffer overflows and off-by-one errors in C programs which involve buffers with explicit size values instead of null-terminated C strings. The real problem with C is that it relies on bare pointers, where it would have been better to use slice-type structures that describe a buffer by pairing the base pointer and size, so that they are naturally kept in sync. This article takes a lot of time to "deconstruct" C strings, but never gets to the real issue. The "stylistic issue" is also debatable. With the indentation given in the example, nobody would think that the "while-loop will loop both if-statements", as the author claims.
- Evbn 14y agoIn short, Zed had all of Rob Pike's sneering anger and none of his technical mettle. What has Zed said about C that wasn't already answered more thoroughly by Go?
- killahpriest 14y agoAnnoyingly clever. Even while running Ito JavaScript I have to debate whether I should be annoyingly clever or not. /* bad use of while loop with compound if-statement */ while ((len = getline(line, MAXLINE)) > 0) if (len > max) { max = len; copy(longest, line); } if (max > 0) /* there was a line */ printf("%s", longest); What do you do?
- jacquesm 14y ago- if you hit a line longer than maxline you'll end up with a partial line in your 'longest' buffer - if the line is so long that it will spill across several chunks of 'MAXLINE' length then you'll end up with the next to last bit - you should probably use a character reader that uses realloc to resize the buffer area until even the longest strings fit or the program fails - your getline invocation is wrong, the proper one is getline(&lineptr, &cursize, fp); And the 'real' getline takes care of most of the above gripes, for instance all you'd have to do is to swap the pointer to 'longest' for a fresh one for the next iteration after finding a new longest entry.
- stevelaz 14y agoI'm SO glad Zed has pointed this out! I dislike code like this and I dislike it even more when people say K&R style is the best, it's classic, believe/follow the creators of the language! Fu that. It's error prone, just like Zed says. Add braces, don't be lazy, it makes the code flow easier on the eye. IMO, In JS, it's painfully dangerous to be "too" clever as well. Sometimes I like to think about it like so: Will someone else "with less skill than me" be able to follow this code after me? Then again, being human I sometimes fail to think this way :(.
- Tloewald 14y agoThe overly clever argument is only valid if you think the point of K&R is to teach good programming style. In fact it's to teach you to read and write C programs. In many cases the learning point is achieved by the reader puzzling out why the code works and learning from this. Complaining that this isn't easy enough is missing the point. Arguing that the language shouldn't allow such constructs is again outside the scope of the argument. K&R designed the language, so presumably they agree with the design.
- NickPollard 14y agoI think Zed is right to point these errors out - there are quite a lot of issues with edge case inputs and undeclared assumptions, that people definitely need to hear about. That said, he appears to be dealing in absolutes too much. If you care about performance (and let's face it, if you're using C you do, otherwise you probably shouldn't be using C) then sometimes you can't handle as much error checking or error correcting as you'd like. In games (where most of my experience is), it's common to have functions that are 'unsafe' by his definition, but that are hidden in compilation units and not exposed in the header, so that the programmer can control exactly where they're called from. If you have a limited number of 'gatekeeper' interface functions that are actually called from outside the module, and these either check/sanitise/assert on their inputs, then the internal private functions can safely assume that they have valid input and just run as quickly and as simply as possibly.
- jacquesm 14y agoThere are many other good reasons to use C besides performance. For instance: - cross platform portability - predictability - reliability - long term stability / maintainability - low run-time overhead, fine grained control of memory
- PommeDeTerre 14y agoDon't forget the people. Long-time C coders are among the best programmers around. It's hard to understand just how damn good they are until one has gotten a chance to work with one or more of them. The good ones know all levels of the hardware and software stack they're working with. Coupling this knowledge with the raw power of C, they can put together amazing and resource-efficient software in very little time, yet without sacrificing maintainability, security, portability and the other factors you've listed. These are truly the people who make the impossible become possible.
- bjoe_lewis 14y agoif this is true, then I'm going to take C more seriously. I have to move myself forward from TurboC programming.
- mtkd 14y agoThis book introduced me to modern languages, from my previous experience coding in assembler. It inspired the millions of lines of code I've written since. I now understand why my subsequent programs, and those of many in my generation, have been riddled with bugs for 3 decades.
- jacquesm 14y agoK&R is hardly the only book written about C, nor is it the best. You should definitely not stop learning about a language after you've read just one book about it. K&R C was best practice in 1980 or so, since then we've learned a lot about C, about what to do and what not to do. If you still program C like it is 1980 then you can't really blame that on a book from 1978.
- rcamera 14y agoI am midway through K&R, but now I am curious, what other book would you suggest?
- jacquesm 14y agoYou could do a lot worse than getting a copy of Sedgewick 'algorithms in C'.
- telemachos 14y agoPopular suggestions (not introductory): C Interfaces and Implementations (David R. Hanson) Expert C Programming (Peter van der Linden) I'm also a fan of C Programming: A Modern Approach (K. N. King) as an introductory book, but it's very different in intended audience from K&R. King is writing for students, and he assumes nearly nothing. K&R are really writing for experienced fellow programmers, I think. So it's an apples to oranges comparison. But King is certainly more modern. He includes a fair amount of C99 material. The writing can't compare with K&R. It's nowhere near as dense or elegant. But if you find parts of K&R a bit too dense and elegant, it's a good trade-off. (I used them both in tandem while trying to learn C this summer. It worked well for me. When King got too verbose, switch to K&R. When K&R lost me, switch to King.)
- experiment0 14y agoTheres an analysis of his deconstruction here: http://functional-orbitz.blogspot.se/2013/01/deconstructing-zeds-k-deconstruction.html http://functional-orbitz.blogspot.se/2013/01/deconstructing-...
- deleted 14y ago[deleted]
- Proleps 14y agoEven if I made a function copy(char to[], char from[], int lenTo, int lenFrom) it would still be incorrect using his reasoning because most of the possible inputs would still cause the software to crash. I could symply add a wrong lenTo and lenFrom.
- guard-of-terra 14y agoGarbage in, garbage out.
- judofyr 14y agoA = {'a','b','\0'}; B = {'a', 'b', '\0'}; safercopy(2, A, 2, B); A = {'a','b'}; B = {'a', 'b', '\0'}; safercopy(2, A, 2, B); A = {'a','b','\0'}; B = {'a', 'b'}; safercopy(2, A, 2, B); A = {'a','b'}; B = {'a', 'b'}; safercopy(2, A, 2, B); This analysis only tries different values of A and B, not the lengths. A proper analysis of "for what values does it fail" should include all parameters. What happens if you do `safercopy(3, A, 2, B)` or `safercopy(3, A, 3, B)`?
- tcarney 14y agoExactly what I noticed. The "safer" function relies on the length arguments being correct, just as the copy function relied on the strings being null-terminated. The safer function is more explicit so less prone to error, but both rely on the programmer doing the right thing, which is what he was trying to avoid...
- yxhuvud 14y agoThe good part is that valgrind will hopefully find that it read outside bounds immediately, as opposed to code depending on correct string termination which will work with correct test cases.
- zeteo 14y ago> we will be modernizing the code in K&RC [...] It will be more verbose, but it will be clearer It's been at least a year since the article first showed up on HN, and the author still hasn't made good on this promise. It's actually hard to do, because verbosity and clarity are usually at odds with each other. One of the main attractions of K&R C is exactly its lack of verbosity, its extraordinarily high content-to-length ratio. In a very short space you learn to do a lot of sophisticated programming, and most if not all code examples fit on one page or less. Of course, optimizing for conciseness has its costs, as anyone who has debugged segmentation faults knows. So you avoid some of this shooting-yourself-in-the-foot that C is infamous for by using various crutches: add an extra argument for some functions, build your own Pascal-style strings etc. And if you pass in external input then you should definitely use some of them, such as strlcpy (which is actually preferable to the four-argument function that this article is getting to). But there are also lots of cases where plain old strcpy will do fine, and for simplicity sake it's better to use it. I believe one of these cases is a learning experience in which you want to get the big story as soon as possible, and are willing to wait until later to get acquainted with the inevitable caveats and detours.
- Ingaz 14y ago>> One of the main attractions of K&R C is exactly its lack of verbosity, its extraordinarily high content-to-length ratio. In a very short space you learn to do a lot of sophisticated programming, and most if not all code examples fit on one page or less. Funny. The same goes for "Programming Erlang" by Armstrong. On contrary: "Erlang and OTP in Action" is quite boring (in comparison with book by Armstrong). Definitely it's not a book for "enlightment" but for practice, sometimes dull: "Do it in such way, you don't need understand why, you'll get accustomed to it in future"
- foxhill 14y agothe point of the exercise is to understand how a copy works, and stylistic issues aside, the point is made. if you supply a function with inputs outside of it's specification (NULL-terminated strings), then undefined behaviour is (by definition) going to happen. besides, what's to stop someone from calling safercopy like so; safercopy(strlen(str1), str1, strlen(str1), str2); then strlen will fail (albeit a bit more safely - perhaps). it's a safe bet, that in production code, we'll not be working with fixed length strings. so we need to get the length of the string somehow. all his safercopy does identify a problem that he himself already points out is impossible to solve - how do we differentiate from a NULL-terminated string, and one that isn't? the only real solution (i can think of) is a string class, where the constructor is guaranteed to return valid (or no) strings. then (assuming other functions can't overwrite our memory - already an unsafe assumption) we could guarantee a safe string copy. programming is hard.
- dakimov 14y agoIronically, I most likely wouldn't hire Kernighan or Ritchie because of their poor programming style.
- oinksoft 14y agoDennis Ritchie created C. Also, you might have a hard time getting him into your office for an interview; he died just over a year ago.
- Turing_Machine 14y agoThey (along with Ken Thompson) created C and Unix, the most successful programming language and operating system in history, both still in heavy use over 40 years later. Long-term success might be a better metric than "style". Just sayin'.
- Evbn 14y agoThey probably wouldn't be interested in your project, so it is a win-win.
- jacquesm 14y agoThe one has passed on, the other is currently otherwise engaged. You couldn't afford them, and you're so arrogant that anybody halfway versed in the art will probably not want to be associated with you to begin with. You'd be HONOURED to work with people of that calibre, but instead you feel you need to show you're better than they are by kicking at them. Trust me, you can't kick that high up.
- mturmon 14y agoYour hubris takes my breath away, which makes it difficult to laugh at your ignorance. Painful.
- dakimov 14y agoI am sorry, there was no 'hubris' at all. You obviously misunderstood my comment along with other few not very bright people. Recently I had to conduct a number of job interviews on C/C++, and then I came across this article, and a thought came to my mind that great people cannot be measured with a scale of mediocrity, and that the mainstream in every field of human knowledge is used to be very focused on non-essential detail. Sapienti sat, but in your case it is the reversal.
- brudgers 14y agoShaw mischaracterizes K&R by making anachronistic assumptions about its intended audience and ignoring the context in which it was written. When it was written, a beginning C programmer was most likely coming from a background in assembly and accessing the computer as a professional in the workplace or a student with substantial privileges. The intended audience was sitting near the cutting edge and was assumed to be sophisticated. Data validation could be left as an exercise for the reader in good conscience. This audience is distinctly different from those who learned programming typing code from magazines and Shaw's current audience for whom he is simulating that experience. Editorially, K&R has chosen to remain a slender tome. It has let others create fat cookbooks and "for idiots". Forty years on, Shaw criticizes the Wright Flyer by the criteria of Second World War aviation. We don't hold K&R on a pedestal because of its pedagogical methods, but because of the power of the language it describes. The C Programming Language was a byproduct of creating a language. Kernigan and Ritchie were programming. Their book is properly judged by different standards than Shaw's educational project. None of which is to suggest that Shaw"s project may not achieve a comparable level of esteem
- mr_luc 14y agoHe mentioned that the intended audience was different, and even talks about it a little -- what he objects to is that people learning C are being pointed to it in a way that removes all of that context. Especially, it seems like holding K&C up as a paragon of "style" offends him. That's what he means when he says it should be relegated to 'history.' I'd submit that K&C is the fastest way to learn C if you knew nothing about C, or the best way to acquire the Zen of C, and that for those purposes it's still second to none, and its conciseness is a virtue ... but that Zed might be right about people pointing to it as a paragon of style to newbies who hope to write it professionally someday. In fact, I'm disappointed that Zed let this project drop. Seemed neat.
- diminish 14y agoyou can always read and benefit from an old text without anachronistic mistakes, and any text written today will also be obsoleted quickly. it is usually better reading the masters than the pupils.
- xentronium 14y agoI don't understand it. His solution to non-safe introduction to non-safe language is to slap defensive checks everywhere? I am not sure that scaring his students shitless by having them check every input is as effective as concise explanation why and when you should check your inputs. Criticizing K&R because of their safety assumptions is a faux pas.
- mikeash 14y agoThis article is bashing just for the sake of bashing. While it pretends to offer constructive critiques and solutions, it fails miserably. One can certainly have productive discussions about the proper way to handle buffers in C, but this isn't it. Zed Shaw should stick to Ruby or whatever area he's actually good at.
- Evbn 14y agoHe did the same thing in Ruby first, before everyone got sick of him and he moved to C.
- wh0cares 14y agoITT: pedants and butthurt
- mkumm 14y agoI have been through both, K&R and Shaw. K&R is the resource I point new developers to who want to learn C.
- coofluence 14y agoI am wondering when will the author take on correcting errors in MMIX code in Knuth's books. He will at least earn a hex dollar for his efforts. Jokes apart, I admire the bravery in questioning K&R C's status. I have only a few personal insights to share as I don't code much these days. 1. I got introduced to C in 80's via some popular book. K&R not only taught me C but also was my entry point into the systems programing world. Before K&R, the BASIC programming books never allowed me to deal with memory or interrupt vectors in the way C allowed me. 2. C has a great power dynamic built in to it. You are on your own in dealing with this power. It's you who crashed the machine, not C and surely not K&R C. 3. Almost every language since C has borrowed something from C. Hence, anytime I saw a familiar notation or code block in any language that reminded me of C, I got confidence that I can learn this language. K&R's many virtues give it an unique status. It did something that no C book or website can do it. It is the word of the language designer themselves. They shared their reasons of the choices they made.
- aufreak3 14y agoThough Shaw raises a valid point based on the fact that we program in more diverse and hostile situations today than K&R did back in their days, I'm unsure how deep one needs to go into program correctness when teaching a language. Wouldn't it suffice, for example, to have program correctness highlighted as a chapter? Take this - > Also assume that the safercopy() function uses a for-loop that does not test for a '\0' only, but instead uses the given lengths to determine the amount to copy. It is possible to write a safercopy() conforming to that loose specification that will not terminate. Just make the test something like "i != length" instead of "i < length". Then you can supply negative length arguments and get it to fail. Of course, that would be stupid, but it already illustrates the art of specification. .... Well, with finite precision integers, "i != length" would terminate at some point due to wrap around, but would take the universe to end if you'd used 128bit integers. To do it even more simply, `safercopy(1,2,3,4)` can crash the program. Is the moral of this story that programs are not valid outside the context they were created for? .. or is it to never use data structures whose integrity cannot be proved without failure? .. or is it that proving a program's correctness using some method only indicates a failure of imagination? .. or, to put it differently, that you can only prove a program wrong but never one right?