I was wondering how this compares with or compliments K&R, when I came across [this section][1] of the book that seems to slap it in the face. Here's a section from the second paragraph:
You see, "K&R C" is actually riddled with bugs and bad style. Its age is no excuse. These were bugs when they wrote the first printing, and the 42nd printing. I hadn't actually realized just how bad most of the code was in this book and recommended it to many people. After reading through it for just an hour I decided that it needs to be taken down from its pedestal and relegated to history rather than vaunted as state of the art.
This seems a bit hyperbolic to me... I just read K&R several years ago during school (could definitely use a re-read) but remember absolutely loving it for the combination of brevity and density of information conveyed. I'm not much of a C programmer though so I don't really know. Could a modern C programmer respond / elaborate?
It's a bit like claiming memcpy() is bugged because it can crash your program. Well, true, it is good style to program defensively against memory corruptions, buffer overflows etc, and in that respect K&R may teach students bad habits.
I think it's a bit much to say that it should never have been written like that, as at the time people knew so much less about building complex projects, sources of error and defensive programming.
Keeping a book up to date takes a lot of effort, as I'm sure Zed (npi) appreciates. It's an old book, it has warts. As you stated, the density of information was what many people like about the book, so any replacement for it will have to replicate that. I don't think the criticism is unfounded though.
Nah, memcpy shouldn't exist (just not for buffer overflow reasons). memcpy is there because someone got their knickers in a twist over possibly being a few cycles faster than memmove, even though the chances that actually matters is tiny, while the chances that you'll accidentally use memcpy when your memory overlaps is much larger.
actually that justification for memcpy is quite poor. usually, performance wise, it is quite easily beatable once you start using the hardware. most implementations I've seen loop byte copies in the most naïve way possible... almost every architecture supports 32-bit copies, most support more... :)
incidentally I've saved some .2-.8ms per frame in a "AAA" (I use the term loosely and with disgust) game title by replacing compiler generated assignment operator in one struct with an explicit 128-byte copy. copying individual bytes is slow, and the compiler is never as sufficiently smart as people claim.
in analagous cases involving memcpy, the fact that memcpy is not memmove saves much less than actually copying the data in a way that squeezes everything out of the data buses. :)
"almost every architecture supports 32-bit copies"
How much of that was true at the time that function was created?
Also, on 'just use memmove': it would not surprise me if ancient Unices used memcpy instead of inlining it to copy really small buffers (such as 14-byte-max filenames), not for speed, but for the byte savings (even if singular 'byte' is the correct way to phrase that). With small buffers, the overhead of that 'if' can become substantial.
Yes I guess that would be more appropriate. Again, it comes down to the "give it the right inputs" argument. At some point you have to trust the user of the function to call it correctly.
Your argument with permutations of inputs is wrong IMO - this argument can be used to claim that any functionality which calls user code (through callbacks, inheritance, whatever, functors) is also defective. Strictly it may be so, but the defectiveness is massively outweighed by the convenience - the most obvious example is calling a late-binding function - but it extends to easier to grasp examples like the C qsort function or EnumWindows in Win32.
Should we not make library calls because the programmer who wrote the library might have, maybe, possibly, screwed us over in some way perhaps, or because the mechanism can be broken with faulty input?
You trust, potentially, the same programmers every time you trust an executable loader to start running your code... which is pretty much every single time.
Aren't you as the person providing the application ultimately responsible for knowledge of what library calls work well and, thus, which you should use? I think Zed's cautionary statements are merely summarizing a bunch of his experiences, ultimately that this library call isn't worth using.
Permutations of input is not "wrong", its a valid mechanism for exposing unexpected/undefined behavior. "Massively outweighed by the convenience" is subjective and, in many circumstances, very suspect.
Your argument does not follow and is an illogical extension. I simply prove that it has a while-loop that won't terminate and use permutations to prove that. The logic proof is valid because I found an instance of the input that would cause it to not terminate.
You can't then extend my argument to mean that all functions should not be called. All functions with unterminated while-loops shouldn't be called, but not all functions.
I remember the gist of the critique being that the K&R book was perfect for the assumptions of its era, but can mislead people who aren't experienced enough to apply its lessons to the modern networked world
heh heh. Who would have expected mercy and respect from the guy that made the (paraphrased) "pro-mo-fo" website (first saw it at Atlassian conference about 2 weeks ago, pretty funny though perhaps a bit rude). I think the guy is more than willing to point out any practices now deemed harmful, tradition be damned. I'm over it.
Guitar however is a weird instrument that doesn't really work with notation, so guitarists have an alternative notation called "tablature".
This is incorrect. Guitar works perfectly fine with standard notation. It may be somewhat difficult to figure out fingerings if the music isn't arranged for guitar, but it's not much harder than playing piano without fingering information. I agree with the overall analogy (that tablature is initially easier than sheet music, but ultimately restrictive), though.
Huh, really? I always figured piano had an unambiguous mapping from pitch to keys, whereas guitar typically has 2 or 3 possible fingerings for a given pitch. I find it pretty easy to get stuck with awkward positions on my first few times through a guitar piece--except those in which transitional notes are annotated with fret positions. Is that what you mean by "arranged for guitar"?
If the pattern is legato or fast and you can't lift up your hands, playing both G and A with the 5th finger will be difficult for most players. The easy solution is to finger the first triad with (1,3,4) or (1,2,4) so you can move 4 to 5. The latter being preferred if possible, to reinforce the weak 5th finger. (1,2,5 is probably the most natural fingering for the a-minor6 triad)
This is a trivial example, of course. With real music it's usually much more complicated and learning how to handle fingering challenges is one of the more important things you learn as a student of piano. Even classics often have fingering suggestions included, if not by the original composer than by subsequent editors.
No problem. I can't comment much on the initial comparison between tablature and fingering since I don't read tablature or more than the very basics of guitar.
It is true that if you map in the reverse direction, tablature can represent playable music even if it's not particularly expressive, while piano fingerings can not. (Well, I suppose you could write music with only piano fingerings and let the performer improvise, but I'm sure that's not the point of this discussion :)
Yes, string number/finger number positions are what I was referring to. And, as Goladus pointed out, getting stuck in those awkward positions definitely happens on piano, too.
No, it doesn't. Go find middle C. See how on the piano it's in the middle of the piano, and also in the middle of the grand staff? Now, find one of the many on the guitar. Not really in the "middle" are they? Even the left and right hand and the grand staff fit better. The location of submediant, inversions, arpeggios, distance in intervals, all map better on a piano than a guitar.
The problem is that the examples and explanations don't justify the level of castigation. Yes, you should always use strncpy() instead of strcpy() for instance, but that doesn't protect you from all bugs. The example of a safercopy() still relies on the assumption that the pointers given to it are valid, and that assumption is just as impossible prove as the assumption that copy()'s input strings are valid. Safercopy() is still twice as safe as copy() and we ought to use it, but calling copy() "defective" is unfair unless you're willing to call the C language defective in general. And you can make that argument! I certainly like languages like Go that have pointers but no casting ints to pointers or pointer arithmetic, so the only remaining danger is pointers to memory that has been freed.
"Yes, you should always use strncpy() instead of strcpy() for instance, but that doesn't protect you from all bugs."
I agree with your argument, but I don't enjoy encouraging programmers to use strncpy and co... the reason is simple, I don't want to encourage programmers to stop thinking about their code. Not having to think about something is great, when laziness is acceptable (RAD, web development etc.) but not in the general case...
Most of the time, the programmer should be smart enough to not fall foul of the problems strncpy avoids. The argument against the copy function in "learn C the hard way" implies that the programmer doesn't understand the code, language, or platform particularly well...
Mine is a fundamentally flawed argument though... as much as I want to live in a world where people can write code properly, the truth is idiots will always be out there. Although, as such, I would rather my code did shoot them in the foot if they were stupid enough to load it up and point it that way before firing... :)
The knowledge is esoteric to what is being done. I know its convenient to believe so, but there are smart people that make mistakes. Misusing one of these methods isn't discouraged because its obvious, its discouraged because it fails in non-obvious ways. Ways that have probably cost humanity many man years of development time.
You're saying that the language should make the programmer's job harder, and that it's a mark of laziness to try to arrange things to make common mistakes less likely?
Human brain has very limited bandwidth. The more irrelevant bs it has to deal with, the less effective is in solving actual problems.
It's surprising that people otherwise living day and night dealing with bandwidth and capacity issues of software systems ignore their own limits on bandwidth and capacity and are willing to put up with random bs in the name of some macho industriousness.
You see, "K&R C" is actually riddled with bugs and bad style. Its age is no excuse. These were bugs when they wrote the first printing, and the 42nd printing. I hadn't actually realized just how bad most of the code was in this book and recommended it to many people. After reading through it for just an hour I decided that it needs to be taken down from its pedestal and relegated to history rather than vaunted as state of the art.
This seems a bit hyperbolic to me... I just read K&R several years ago during school (could definitely use a re-read) but remember absolutely loving it for the combination of brevity and density of information conveyed. I'm not much of a C programmer though so I don't really know. Could a modern C programmer respond / elaborate?
[1]: http://c.learncodethehardway.org/book/learn-c-the-hard-waych...