Over the years, I have acquired most of what people call seminal C books, and after reading them, I recommend the following two books to people who already know programming:
The first is clearly written and focuses on ANSI C (lexis, syntax and semantics), with a healthy dose of software engineering education along the way, which you may already have. (It does not yet cover the latest ISO changes.)
The second book is a true gem that teaches even seasoned programmers how to implement correct, clean and portable components as libraries in ISO C, with plenty of source code to show how the true masters of the language craft the finest code you can in C.
Most books how fragments of linked list implementations, but only this book shows complete a whole range of important ADTs you cannot live without (e.g. List, Ring, Atom) in C portably, with strong typing, bullet-proof error handling etc. (Hanson is also the author of lcc, a portable C compiler.)
I'm actually surprised that these are both are so little known, and that the second one hasn't seen more editions.
One can only learn a language one time, so I've got a hard time recommending first books.
But I definitely second the recommendation for the second book here ;)
Hanson's style is a bit idiosyncratic, with his somewhat Modula-3-ish naming style and literate programming use, but there are a lot of usable data structures in there, and I especially like that the focus is on the module level, not on the algorithms themselves.
Now, having said that, if you're in a particularly weird sub-domain of C, books from there might supersede or at least color a lot of what you're doing on a day to day basis. In the days of yore, if you wanted to do Win16/Win32 programming, getting to work with your Petzold early is a good idea, and I would assume the same to be true if you do glib/gobject based Gtk programming.
Two free resources that looked good to me and that I forwarded in the past were "An Introduction to the C Programming Language and Software Design"[1] by Tim Baily and "Beej's Guide to C Programming"[2]. Those are a bit more concise than your average intro to C book, but for some paths (prior experience, quick headway to C++ etc), they might be sufficient.
I think it's a lot less useful for that. The problem is that doing that with C is that you're already strapping some lead to your feet from the very start, so a lot of what CII does is removing those weights by adding stuff from other languages, either by merit of coding style or new libraries. This would already go to far for a lot of C purists, but it doesn't totally try to work deny the language you're working in.
So it does basic error handling & modularization. You get that "for free" in a lot of other languages, or sent in totally different directions anyway (good modularization in OO and/or FP looks somewhat different)
Then there's a lot of practical data structures and algorithms, with good references to other versions and sources, so both something you could use immediately but also serving as jumping off points to advanced study.
So I appreciate it a lot more in combination with bare bones C than with other languages or even C environments with more standard library functions.
For style & technical guidelines, I think Code Complete is still a great book, or the Pragmatic Programmer. For a trad-C book, maybe even Pike's Practice of Programming. Avoid anything avuncular.
Of recent years (and not including OO stuff in general), Ousterhout's "A Philosophy of Software Design" packs a lot in a few pages.
However, i am not clear what you mean by "language agnostic"? Obviously Code cannot be language independent but if you can read C then you should be able to translate the data structures/algorithms given in this book to a language of your choice; the difficulty might vary (eg. how would you represent pointers in your language to implement say recursive tree structures?) but it can be done. The data structures/algorithms given in Hanson's book are "industrial strength" with no missing parts (i.e. no exercise left to the reader). So you get complete code all written in the "Literate Programming" style which takes a little getting used to but is eminently worth it.
Pretty Neat! I had come across similar techniques in Arthur Whitney's work (past HN threads).
However i interpreted the original "language agnostic" question as maybe wanting to implement the C code in terms of some pseudo-code and hence my question. The phrase also seems to have another meaning: https://en.wikipedia.org/wiki/Language-agnostic
I wouldn't recommend it for that (see sibling comment). Reuse isn't as idiomatic in C as it is in other languages, for reasons inherent in the language, e.g. see all widely used C codebases
I read the Hanson book ~10 years ago (he was briefly a coworker). It's interesting, and maybe worth checking out for experienced programmers, but NOT for someone who wants to learn C in 2022 and be productive.
The style doesn't match any C code you're likely to see. It basically presenting an STL-like set of data structures and standard library in C, not C++.
That style of programming isn't idiomatic in C. One thing that took me a long time to realize is that C is NOT a modular language! The best way to use C is to roll your own (or copy and paste) data structures for your problem, not try to use canned ones!
If you want the latter approach, C++ is better. It has better support for type safety, abstraction, and modularity (at great cost).
i.e. if you compare code of commonly used codebases like Lua, CPython, sqlite, Redis, BSD kernels, Linux, etc. you will see patterns, and they don't look anything like Hanson's book
I don't take the point of this book to be "how to implement an STL-like data structures and algorithms library in C"---although you were his coworker, so maybe you know better. I've always viewed it more as "how to build data structures with useful interfaces in C". In the book, the set of libraries presented are an example which illustrates how to approach this using the features that C provides. If you are new to C and don't know how to manage complexity by building up useful interfaces for your data structures, reading Hanson's book is a reasonable way to get some ideas. Another way to go would be to just read through the source of any of the libraries you mentioned, all of which do in fact use ideas which feature in this book. This is obviously not to say that Hanson's book originated these ideas. I also think focusing on the exact style of the code in this book is maybe a bit of a red herring.
I'm sorry, I don't completely understand what your point. You seem to be alluding to "what C is actually good for", but it's not clear to me what that's supposed to be. I understand that you think C should not be used to develop generic components that are reused, and that data structures and such should be re-rolled or copy-pasted each time they're used.
On the other hand, you do hint that you think C can be used reusably, provided that the ABI is the focus. I agree with this.
In my opinion, the techniques discussed in the book are in fact useful techniques for accomplishing exactly this end. If you want reusability the ABI way, what you want is a small surface area which changes extremely slowly. Ideally, you want each ABI to be a subset of the last to avoid breaking changes. OK: accomplishing data hiding via opaque pointers in tandem with a simple, high-level interface is one way to accomplish this, and is the main lesson to be learned by reading this book. (I would argue you really only need to read a chapter or two to learn it, but that's a different story---I would argue that this book is in fact not very good for this reason, but whatever.)
If you look at most reusable C libraries, they take this approach. Obvious this book takes a bit of an extreme approach where as much of the details of a data type are hidden as possible, but there is no real reason to do this provided that the details of a type are stable. Then it's OK to expose them as part of the interface.
I suppose I take your point that in reading this book, one might get the idea that it's a good idea to develop an STL-style library to use with C and run with that. On the other hand, someone who is learning C might just as well read many books about it (or read lots of code), and be able to contextualize this book well enough to understand that it should be taken with a grain of salt.
I think it's also important to point out that there are exceptions to these "rules". GLib is a generic STL-style library written in C which seems to be pretty widely used. At the other end of the spectrum, you have more people (I think mostly in the games community) developing "header only C libraries". YMMV.
So basically, use EITHER idiomatic C, which means long functions / few internal interfaces, reuse with ABIs not APIs, bespoke data structures, etc.
OR use C++ and ADTs (type safety, abstraction, polymorphism). Or use Rust if you don't need compatibility.
So books like CII are of very limited use, especially not for beginners. They will be fighting with the language and not understanding what it's about.
----
To sum it up, C is better for coarse-grained reuse (deep interfaces a la Ousterhout, Unix file system) than fine-grained reuse like CII (hash table, array, set)
Why is "idiomatic C" equivalent to "long functions and few internal interfaces"?
The whole point of having a small surface-area for your ABI with plenty of detail hiding is that you are free to do what you like beneath it. If the code uses a struct named "hash_map" that takes a generic hash function and stores void pointers, but runs fast for my purposes, what do I care? These techniques give you a controlled way to build black boxes.
C is a very flexible language, and it is possible to build up quite a variety of patterns in it. Your job as an engineer is to look in your toolbox and select the best tool for the job. Adhering to a "correct idiom" gets in the way of this.
And I just looked it up -- C was invented BEFORE abstract data types were! This explains a lot.
Programmers think "hash table", stack, queue, set, etc. are "the way you program", because that's how it's taught in CS 101. It's also asked a lot in interview questions.
But that line of thinking was invented AFTER C. I recall that Liskov did foundational work on ADTs, and Wikipedia agrees
Programming with abstract data types (Liskov and Zilles, 1974)
At MIT she led the design and implementation of the CLU programming language, which emphasized the notions of modular programming, data abstraction, and polymorphism
-----
Meanwhile C was developed around 1972-73 to make the Unix kernel portable. It's a minimal layer meant to generate machine different machine instructions that will work on machines; it doesn't feature strong abstraction.
So what Hanson was trying to do is to add abstraction to C, but you need a language more like CLU or C++ to do that. Arguably Bjarne was "doing it right" -- actually changing the language.
----
HOWEVER I also had a recent experience that vividly shows the folly of ADTs even in C++, a language designed for them.
In C++, unordered_set<void star>::insert() is shockingly slow -- slower than malloc(1) !!! And it is slow BY THE SPEC. Because of the abstract operations that the C++ standard requires (e.g. iterators and invalidation), you have to use a slow closed-addressing/linked list implementation that allocates for EVERY element !!!
I hit this when working on the garbage collector for https://www.oilshell.org/ -- we're making a fork() friendly collector (no intrusive mark bits) that works with arbitrary addresses returned by malloc.
It made some of our benchmarks 10x slower in TOTAL runtime!
So basically the hash table in C++ is a shockingly bad default, and it has to with a bad ADT design.
Doing ADTs in C is even worse. Hanson's book is more like a thought experiment in merging two distinct lines of thinking -- not something you should actually use and base your code on!!
----
This would be a good blog post -- I would title it something like C Was Invented Before ADTs, and C++ Isn't Great Either
I don't see how this observation that C++ STL containers aren't very good somehow "shows the folly of ADTs" ?
The fact these are abstract types doesn't seem relevant, if you explicitly implemented this feature set for, say, the long type and named it "chubots_long_set" it would still suck, but it's not abstract, the abstraction played no part.
I don't have any problem with ADTs or abstraction ... In fact I'd say the strength of C++, and why I use it, is precisely that you can create your own abstractions, more so than C.
I'm just saying that using a canned set with "many useful" operations is suboptimal, design by committee is suboptimal, etc.
As opposed to analyzing the ops your app needs and creating custom data structures. i.e. there are many different ADTs for "hash table" or "set"
That might seem obvious, but it's not how software development is being done today. There's a lot of code reuse that leads to suboptimal software; it would actually be better to copy and paste and refine more.
I still think you're mostly conflating "the C++ standard library provides bad implementations of this stuff" which is famously true, with "generic solutions are just bad" which is at least non-obvious and I'd argue generally false.
> copy and paste and refine more.
That doesn't get you better algorithms, which is what you need. If you start with the C++ standard library's unordered_set implementation in a source file and you "refine" that you won't get from there to absl::flat_hash_set except in the same sense you could start with the footage from "Grease" and end up making "Bugsy Malone". Start over with a different design.
Picking from a handful of options like absl::flat_hash_set rather than always hand-rolling is a clear win for productivity. As with anything else in performance, measure first and only solve problems you actually have applies to the idea that maybe the choice doesn't work - even for the abysmal std::unordered_set if you don't have measurements showing it's a problem then it probably just isn't a problem.
I am quite sure JOVIAL, ESPOL/NEWP, ALGOL and PL/I dialects, Lisp, which predated C in more than a decade have enough abstraction capabilities, even by their state in 1972.
Interlisp initially created in 1970, besides the lists everyone knows, introduced atoms, string, arrays and compound data types, which alongside macros provided the necessary foundation to create abstract data types
> The language which most closely resembles, in form, the language presented here is SIMULA 67. 8
SlMULA class definitions have many similarities with cluster definitions.
Yeah it doesn't, that is why there are enough papers and books how to implement them with incomplete types, and translation units with statics as poor man's replacement for modules.
Again, there is already a well known solution for this problem -- C++
And it's trivial to upgrade to from C
So basically, use EITHER idiomatic C, which means long functions / few internal interfaces, reuse with ABIs not APIs, bespoke data structures, etc.
OR use C++ and ADTs (type safety, abstraction, polymorphism). Or use Rust if you don't need compatibility.
So books like CII are of very limited use, especially not for beginners. They will be fighting with the language and not understanding what it's about.
Note the above book is good for its quality as a reference, but not enterily considered as "modern". For a more updated approach I would also recommend:
Is interesting from a historical perspective, but considered as not a recommended resource for learning C.
Edit: If you are learning C there is something quite important you need to know... C and C++ are two completely different languages, and that is how you should approach your learning. In other words, don't go learning the C in C++ :-)
I found the first book in a second hand shop a few years ago, but unfortunately it didn't came with the diskette. Any change you can upload the code from the diskette somewhere or point me to where I can find it ?
Despite what many think, I'd say the "The C Programming Language (2nd Edition - ANSI C)" by Brian Kernighan & Dennis Ritchie (K&R) book is still a good introduction to C. It doesn't go into best practices, or other forms of more advanced C programming, but it teaches C well. I'd pair it with "Understanding and Using C Pointers: Core Techniques for Memory Management" by Richard M Reese to get a good understanding of the pointer underpinnings of C. They are both small, well written and easily digestible.
I read the first edition of K&R when it came out, and switched to C for a computer algebra system that got me tenure as a math professor. K&R is a religious scripture, and remains the benchmark against which I measure all other programming books. It's in the same league as the aspirational SICP, and a far easier read.
In college I'd programmed Fortran on punched cards. APL was like dropping acid, and I worked one summer as a commercial APL programmer, but it wasn't appropriate for what I needed. Pascal didn't quite fit either. C was in that context mind-blowing. The future had arrived! I had full access to the machine!
If one is learning to become a plumber, one might not be so interested in how plumbing worked in 1840. If however, one is a herding dog, and needs serious work to avoid getting bored and destructive, reading K&R would put C in historical context.
Later, as I depended on C, I bought each edition of C: A Reference Manual by Harbison and Steele, and read it as if I were studying to be a lawyer.
I now program in Haskell, and wish I was equally versed in Clojure and Rust. If I were returning to C, I'd instead choose Rust. For my work, I have a free choice.
> I bought each edition ... and read it as if I were studying to be a lawyer
This made me laugh. I always described it as feeling like a collector/completist, but lawyer works too.
Visitors to my home office (back in the dark ages) would often comment on the growing set of editions of "UNIX System Administration Handbook", lined up side by side on my shelf.
Honestly at some point in editions 3 through 6, I was just buying the new ones for the shelf art.
I will be a contrarian - but as you pointed we are many - and recommend against K&R. C is a simple language to learn but a hard language to master.
C has few concepts. You can learn to write C in an hour with a couple of web pages. Then you have to learn how to implement the most useful data structures but that’s not difficult either. What’s hard is writing safe code in a maintainable way and K&R does very little to teach you that properly.
The C89/99 standard is rather small, it is relatively easy (certainly compared to C++) to know it almost fully. But the devil is in the details, it is very important to fully understand how C treats pointers, and how close to the hardware the language is, this is where the caveats are.
C doesn’t do anything particularly strange with pointers. Most of the weirdness comes from arrays not actually being arrays and sizeof being a primitive with a surprising behaviour in my experience. Apart from that you have to understand the difference between the stack and the heap and how your system treats malloc (well understanding that it can fail goes a long way) but that’s pretty much it.
There are probably some sharp corners I’m not thinking of around type conversions but that’s the kind of thing you expect from a strongly typed language.
Most of the caveats are in how compilers treat C rather than how close it is to the hardware to be honest.
Indeed C doesn't do much with pointers, it is very close to a cpu register containing a memory address. If you are used to higher level languages, it is odd. You can simply cast a pointer to one type to a pointer to another type. This is very often used to introduce OO like behaviour, but you need to understand how it works under the hood. Pointer arithmetic is sensitive to types though, so C is a bit smart in that way.
I've read a lot of books about programming over the many decades I've been doing this, and the K&R book still stands out as one of the best examples of how to do a good job of documenting the tool you've just built. The K&R book gave me the heuristic of "read the book by the folks who wrote the language". That heuristic turned out to be mostly wrong. But the book has stood the test of time.
I read The Awk Programming Language shortly after K&R and came away with the idea that I should consider reading whatever Kernighan (co)wrote. I don't think I got further than The Practice of Programming but that was three winners in a row...
Here's another vote for K&R. If you want to understand why C and UNIX were so successful, it comes down to simply excellent communication. And remember - no web, no internet, no WYSIWYG word processors, etc. at the time.
To appreciate C properly, I think at some point you need to become a bit of a historian. Look at BCPL. Look at what assembly listings looked like or old Fortran. Read some of the very early papers from C and UNIX history. Check out Lions' commentary on UNIX. When you see a little of the problem C was invented to solve, it becomes a lot easier to understand why it is what it is. There's a lot of angst about why C allows Bad-Thing-X or doesn't have New-Fancy-Thing-Y. Nobody seems to remember what a change it brought to writing systems code.
Then you can compare it to things written in modern C (C99+) and see that yes it has evolved quite nicely and can, if used correctly, be a nice application language too.
If you are starting with C I second this. C is a really good language to learn because it is not as far from what a cpu does. You end up understanding more of the workings. When someone says "garbage collection" you understand. In my opinion understanding an assembly language and C are fundamental to programming.
Fully agree that reading K&R is a must, because it puts C in context, it gives you the background that explains why C is like it is. Also, as a non-trivial bonus, it teaches you, by example, how to write great technical documentation.
If you must learn C, which is probably sensible even if you never code in it, this ^ is the one to go for. I liked "deep C secrets" by Peter van der Linden some years ago.
This representation for the space U+0020 was very common historically, because a space doesn't look like anything at all, especially if sometimes it's important whether there is no space, one space or two spaces, explicitly showing each space can help.
I am surprised to see nobody mention Practical C Programming (https://www.amazon.com/Practical-Programming-Does-Nutshell-H...) by Steve Oualline. Before I studied computer science in school, I read this book and successfully taught myself C. Readers here may consider it to be a bit remedial, but if you’re struggling to understand the other recommended books, start here. I attempted to read K&R half a dozen times back then, unsuccessfully. Looking back at it now, I can see why: K&R’s examples are hard for true beginners to wrap their minds around. I now teach C as a part of an upper level undergraduate course; the key to understanding most of C’s counterintuitive behavior is to have a clear understanding of its relationship with memory. In fact, I quite like C. It is simple and elegant in many ways. It is also just super unsafe.
The C programming language, 2nd edition. Then if you are into BSD/Posix,
Advanced Programming Unix environment. Here's a course on C programming
for Unix, NetBSD actually. You can install NetBSD with ease.
If not, head to sdf.org and try getting an user account.
You can validate a basic account for cheap. But if you help
the rest of the community with programming under Unix, you may get
a validated account for free.
Do the Unix programming that after TCPL 2nd Ed, BTW.
https://stevens.netmeister.org/631/
This is my favorite of the bunch. I remember picking it up as a long time C dev hoping to learn a new, modern approaches. It helped hammer home that you can only modernize a 50 year old language so much. I didn't come away with from it with a lot of new tricks or techniques. It's just a dated language that has a very specific time and place and when using it, you have to deal with its age.
I needed to re-learn C programming for a mid-career change. This book was a life saver. I will never forget how details the explaination was about printf. That book is incredibly thorough.
"C in a Nutshell 2nd Ed" (O'Reilly, Prinz & Craqford, 2015) is a good reference although maybe not the best for a walk-through learning experience. It also has good chapters on tooling (gcc, make, gdb).
There's a recent book out I came across called "Bare Metal C" (No Starch Press, Oualline, 2022) which unpacks embedded programming in a very readable manner. I imagine a lot of, if not most, C programming these days is done in the low-level embedded world, and this book clears up a lot of the mysteries.
If you have ADHD like me I would recommend "Head First C" from o reilly. Was very visual and covered a lot. Then I continued with The C Programming Language. Sadly I haven't found something similar for rust yet.
it's the dirty little secret in my programming library but these have small, digestible chunks that I can read or do in 5 to 10 minutes at a time and then walk away and do something else (or do more, if my brain is firing right that day).
I never used to need to study this way when I was younger, but things change now that I'm older and I've adapted =)
I read K&R 20 years ago and today it's still the closest programming book to my heart -- yeah there's more modern choices but the exercises are unmatched, it's short, and it's one of the most fun books you'll ever read.
The standards for C99, C11 are open. Look up what you don't learn in them and grab one of the other more modern suggestions from here for that, but K&R is a must have for any C programmer.
You probably won't be able to put the book down if you start reading it.
Technical aspects aside, K&R has what some modern books seem to lack: a really clear and comprehensive layout. I've picked up a few modern books and immediately noticed how they make use of thinner (thinner != smaller) fonts that make then hard to distinguish.
At first I thought it was my fault for being old and wearing glasses, but as soon as I opened back some other old books like the K&R, the Niklaus Wirth (Algorithms+Data Structures = programs) and a couple other ones, I could read again without effort at the same distance wearing the same pair of glasses. Why do they use fonts that thin today?
I personally quite like James Aspnes' Notes on Data Structures and Programming Techniques, it teaches you enough C to be dangerous and proceeds on a whirlwind tour through most of the data structures you'll encounter in the wild.
I would like to take it a step further and ask a question that has been bothering me a while. On my time in the academy I studied the following two books (regarding C):
In combination with other classes (and books) on networking, operation systems, data structures we covered a big variance of use cases with C. My question is: How do I take this to the next level? For example I feel I never missed a concept from those classes but when I see C code posted on a thread here it's probably something completely unreadable and complex. Can anyone provide resources to advance a little? What should I study if my goal is to make good and useful modern C software?
I feel like my typing is a bit abstract but I will be happy to clarify.
[1] is a very good book - it's not really one for learning C though, it's more for when you know C and you need to use it in - well - an advanced unix environment. Sort of like you shouldn't be learning how to use a scalpel in an operating theatre :)
Hm, C language (itself) hasn't got much major changes since ANSI standardization. I would say that one is able to grasp changes relevant to him, by whipping through the changelog for 10-15 minutes
Edit: If you are critizing K&R for not teaching how to write safest code then, well, you are right. It is simply not meant to do that
You're right on the security part, and that C authors hardly care about it is visible on the language itself, naturally the book wouldn't be any better.
I would say "Effective C" is a good book (and Robert is awesome) but personally I feel like I learned the most by writing and disassembling small quines under different compilers and flags more than anything else.
Also, reading through some codebases gave me good (and horrible) ideas and habits. However, C projects are usually messy and I am not sure whether recommending it would be helpful or just confusing. But I would say TenDRA, BearSSL, pkgconf, s6, kivaloo, apk-tools, libsodium, musl, OpenSSH and cosmopolitan libc are worth checking out.
Finally increasing my tiny understanding of architectures, memory models and decades of safety issues was significantly helpful as well.
Since this seems to come up a lot (hence, Steve's blog post), I'll link to "C is not how the computer works" https://steveklabnik.com/writing/c-is-not-how-the-computer-w... Note: neither I nor Steve are discouraging anyone from learning C. I'm learning it myself, but some caveats are in order.
Not to derail the OP too much, but I've also been C-curious for a while, but specifically for writing wrappers C++ projects (for FFI calls from rust, swift, etc.).
I've written a few such wrappers that work, but I have no idea what I'm doing WRT const void * et al. and would love to understand it a little better.
Are there any of these resources that would help me understand how to write better FFI code in C, while maybe skimping on details that are less important if I don't plan to write much code that is "C first"?
Thanks for any suggestions. Apologies in advance for wrong terminology and generally being a noob.
Be very careful about strictly following the rules for FFI since you are bridging two different languages with their own semantics and runtimes which could be very different from each other. See - https://en.wikipedia.org/wiki/Foreign_function_interface
The key is to understand what a ABI is and how the C ABI has become sort of de-facto standard for FFIs.
Thanks for the links -- I've been putting things together here and there from a combination of SO and the Rust user forum, which is why I was wondering about a more formal reference focused on this use case.
You are on the right track; there is nothing difficult here but merely a bunch of moving parts to keep track of and understanding how they interact with each other. The SO articles i linked to earlier are very relevant and you should follow other links from their pages.
That said there is one other crucial point to keep in mind when calling C++ from C. Using "extern C" for function declarations informs the C++ compiler to emit code to C ABI conventions so it can be called from C code. But what about data structures which you might pass and use between C++-land and C-land? They have to follow strict "POD" criteria for compatible memory layouts. See for example - https://stackoverflow.com/questions/49407290/memory-layout-d...
Follow up question should probably be what is a good IDE setup for plain C. CGO support in VSCode for instance never really worked for me. How well does C/C++ plugin by Microsoft work on linux?
Your first C program should be written in an editor like vi, and compiled with cc at the command line. Use tools like IDEs when you're ready for more sophisticated programs and really need their help to manage the complexity. If your only exposure to C is through an IDE, the IDE can hide important things you really should know (that's kind of its job).
I knew a girl in college who was really smart and on a CS major track. At the time everybody used Borland C++, and some people's first exposure to C++ -- or programming in general -- was through this IDE. She asked me if her C++ classroom exercises would compile on a laptop. I said it should be fine. Then she said, "But isn't there like a chip or something in my desktop that compiles the programs?" I was a bit flabbergasted that anyone would think that, but simply replied that the compiler was just another piece of software that will run on any PC.
People come in to programming and they don't understand what's going on. A lot of details are hidden with development today. And it's great that we have the IDEs and so forth to help us not be bogged down in these details -- but a greater perspective is useful to any programmer, and that's why I and Charles Petzold and some other programmers recommend that your first, simple programs be done "the old fashioned way".
(Come to think of it though, maybe if there were a compilation chip -- an FPGA or something -- the compilation times of C++ and Rust could be mitigated...)
I'd recommend starting in VSCode with the MS C/C++ plugin (this works across Windows, Linux and macOS) and do the very first steps without a build system (the VSCode C/C++ plugin has some support for building single-file projects).
Or just build in the terminal with:
cc hello.c -o hello
After those first steps I would recommend installing the MS CMake Tools extensions (https://github.com/microsoft/vscode-cmake-tools/blob/main/do...), this automatically discovers CMakeLists.txt files in the project and then uses those to setup build- and debugging targets and configure the C/C++ language server / Intellisense.
I prefer this setup because it is more 'transparent' than a 'fat IDE' like Visual Studio, Xcode or CLion, but YMMV of course :)
Mentioned by most, still another vote for K&R "The C Programming Language" - for the sheer joy of it! It still puts a smile on my face recalling how much fun it made learning C. :-) Don't think I've read as measured and not-boring for another language since. Close to as much fun to read was maybe Steve Maguire's "Writing Solid Code". Not strictly about learning C, but the examples are all in C. (some people dislike it's K&R not ANSI C)
As someone who teaches C to professional engineers, I'd recommend "Hacking: The Art of Exploration" by Jon Erickson. The first 100+ pages are the most succinct coverage of C I've ever come across (if you can already program in other languages).
Also, by following many of the exploits covered in the book, you get a real grasp of what is happening at the machine level - which is one of the major reasons you'd choose C over something more modern.
I'm a C++ programmer that's not done anything directly in C. I stumbled by this book Fluent C By Christopher Preschern just published this month and I thought the organization looked rather interesting. It covers many programming concerns and details several patterns for dealing with them. One thing that's neat is it often refers to open source projects where the pattern is applied so one could go take a more detailed look.
The problem with K&R is that it stops at C89, while C99 almost feels like a new and much "friendlier" language (mainly because of compound literals and designated initialization).
K&R 2nd Edition is still a very good read of course, but mainly for all the little interesting details that are not directly related to the C syntax, but there really should have been a K&R 3rd Edition when C99 came around.
From ANSI C to C99 there isn't many changes. Just read K&R C 2nd ed and then the book on Modern C which can be understood in a literal afternoon after doing the K&R one.
Those standards barely change anything. You could discuss the “new features” over the last 30 years in about 10 pages after learning the fundamentals. (10 pages may be high.)
Some of the best programming books date from that era. Programming hasn’t really changed all that much in decades. New tools and languages, sure. Mostly retreads or derivatives.
If you don’t mind, I’d like to ask you why you want lo learn C. I’m asking because I’d like to improve my C but I can’t decide between that and learning Rust.
Why not both? Being able to better understand and appreciate Rust is one of a couple factors motivating me to learn C. The others being gaining historical context when analyzing modern c-based languages, being able to effectively read from some of the greatest codebases in the world, and the desire to learn a language that's small enough to fit inside my head and can run anywhere.
Well I don't think they were asking in an adversarial way like you might find on SO.
Seems as if they just wanted some insight into my decision making process and why I chose to do x over y so they can better make the decision themselves.
I've read but not written any c since learning rust. I've written some c years ago.
I do feel that if I were to write more c now it would be better for me knowing rust and having a feel for designs that pass the borrow checker.
Unhelpfully the only c book I've read was the k&r book. It seemed fine. Perhaps the question would be easier to answer if the reason for wanting to learn c was given (eg to contribute to existing c based project or to write embedded code)
Learn C by hand and just writing toy programs. Then read Cert C to learn coding patterns. C requires unique coding patterns compared to other languages, most relating to safety or language features.
The first time I was interested in programming I started learning C. I was 14 and what I used was "The C Programming Language" book. It was a great experience,
C the complete reference - it's part tutorial, part reference, part project book. At the end, there is project where it walks you though making a mini-c interpreter. That project is very approachable and to be honest - that alone is worth the price of the book.
As a matter of fact, that's part of the reason I want to learn C. I started learning Rust, and see a lot of potential, but ultimately find I lack the context to:
1. Appreciate the language.
2. Really understand what's happening in memory.
I hope that learning C will provide both of these things, while also gaining the benefit of learning one of the only widely used languages that I can fit in my head all at once.
Learning more programming languages will make you a better programmer irregardless as it will expose you to different programming patterns, paradigms and the limitations/improvements that result from those.
This is especially true for a popular language, no matter if the language is considered "good" or not. This allows you to understand the libraries/code you will encounter down the road.
I would only discourage one from learning a programming language which is tied to a specific commercial product (most of those just die in the same niche and thus lack the practical utility).
King is good, also Seacord’s books: Effective C and Secure Coding ..
Just ignore suggestions (parroting really) for K&R. It’s probably the worst introduction to C when it comes to good habits, undefined behavior and writing secure code, since it doesn’t even attempt to cover any of these but presents a total facade regarding what programming in C is like.
K&R has created generations of people who call themselves C programmers but are essentially clueless when it comes to deep understanding of the language. Anyone that recommends K&R today is someone you want to avoid taking any advice on programming from.
It's hard to take someone seriously or believe that they've even read the material when they say stuff like this. K&R (2nd ed.) is constantly calling out undefined behavior, etc. and gives you information you need to start out on a path to be a real stickler about the language.
I spent time on my own learning C from K&R since the CS program at my school didn't expose anyone to C (or C++) during the first year. Later, in e.g. the computer architecture course or the data structures course when C was being used liberally (and sloppily), and as I was running across lots of C in the wild, I was continually frustrated by the frequency I saw people relying on common-but-undefined behavior that's explicitly identified as unsafe/unreliable in K&R.
Here's my advice to the original poster:
If you come across unsubstantiated comments telling you to ignore K&R for all the bad things it contains, then ignore those. Or you can ask for evidence of the bad things they say about it. It's become a meme in the last few years to write meatless swipes about how bad K&R is, but in all the times I've asked for evidence of the things it's purportedly rife with, no one has ever produced anything. The most egregious thing I know of is that it uses `gets` and `puts` in some places—while taking extra effort to tell you it's wrong to use them. This is arguably exactly how do-not-do-this wisdom should be imparted.
There is one criticism that holds true about K&R, if you're being generous (to the naysayers, that is), which is that it really does just teach you C the programming language (with some extra stuff about the UNIX system interface at the end). It's not a general software engineering book. You don't learn about build tools or how to structure large, maintainable programs. It exposes you to the fact that C has various nooks and crannies, and it teaches you them, and that's all it really tries to be.
> which is that it really does just teach you C the programming language
A gross misrepresentation that, taken at face value, can only lead to a perpetuation of disasters committed by the sort of C programmers that K&R has produced in droves.
Just because there are a few mentions of undefined behavior in K&R, doesn't mean the concept is addressed or even identified as an issue. There is not a single mention of the "C abstract machine" (what is that, the K&R C programmer wonders), pointer provenance, aliasing, or pretty much any aspect of the language that has emerged in the last 20 years as of supreme importance to correctness and security.
As someone else wrote in this thread, the entire book is hopelessly obsolete and should be retired to the annals -if not the garbage bin- of history.
But what could a book last updated in the 1980s have to say about this subject? The phrase falls out of DR260 (Defect Report #260 in the ISO C standard) which wasn't even raised until this century.
Here's the mention of "provenance" from what WG14 (the committee for the C standard) wrote for DR260 back in 2001:
"the C Standard does not prohibit an implementation from tracking the provenance of the bit-pattern representing a value"
Vague right? And it's technically true, the C standard says nothing whatsoever about this subject. Of course it also doesn't have anything to say about which Pony is Best [Princess Celestia is Best Pony, don't @ me]
You might assume after DR260 the C standard was fixed. Presumably C11 and so C++ 11 and modern languages in this family all spell out how provenance works exactly right?
Nope.
The status quo is that the standard appears to say pointers are basically just addresses, but they clearly aren't in practice in your C or C++ compiler. If you raise bug reports about this, your compiler vendor will say it's provenance and, if you're persistent and they don't just stop answering you, eventually point to DR260, the unresolved defect from 2001.
There are folks trying to produce a consistent model which is somewhere in the ballpark of what programmers expect to work versus what compiler vendors actually deliver, so that both will be happy or, if not happy, at least both will agree on what the situation is. This model is some variation of what's called "Provenance Not Via Integers" but it must have exceptions because clearly sometimes provenance is transmitted via integers, and how that works must be explained too. WG14 did not take this work for C23, but it will become a Technical Specification in the C23 era. If compilers implement this TS, and if programmers prefer having at least the guarantees from the TS over a shrug emoji then perhaps some day it becomes part of the standard itself.
K&R mentions that you can't go around pointing to things of the wrong type (but C programmers do it anyway, and then doubtless you'll say K&R didn't warn them) and that you can't use pointer arithmetic to get at objects which are out of bounds (again, C programmers do it anyway).
The language is unsafe, and it's poorly designed in various ways by modern standards, but the OP didn't ask "What language should I learn?" they have apparently set their heart on learning C and so it's very strange to imagine the goal is to learn a good modern language which C isn't.
The question that I'm answering is "Best book to learn C in 2022?". K&R written as you say in the 1980s (the first edition in the 70s) is not that book, there is far better written material out there: One can get a solid grounding in C reading Seacord's books and John Regehr's blog without even needing to know who K and R are.
I'm guessing based on the comments to my posts that some people are upset due to K&R being their first introduction to C (or their first introduction to technical writing) which leads to emotional responses that miss the point.
So let me re-iterate that point. K&R is a terrible C book in 2022, and should not be recommended as an introduction to the language. It wraps C in a veneer of simplicity-hiding-untold-complexity which leads readers thinking that the language is much safer and much easier to deploy than reality dictates.
This has proven to be disastrous and something that we should point out and drive newcomers away from.
> I'm guessing based on the comments to my posts that some people are upset due to K&R being their first introduction to C (or their first introduction to technical writing) which leads to emotional responses that miss the point.
That's a shamelessly self-serving assumption.
When confronted with a claim/argument that is not favorable to your position, you have (at least) two options: one involves employing humility and intellectual honesty in an earnest attempt to resolve the conflict, and another involves inventing a convenient narrative in order to avoid the conflict. An example of the latter would be a stand-up comic who consistently bombs on stage and chooses to cope with it by telling themselves that the audiences are just too sensitive for the comic's edgy-but-otherwise-brilliant material. An example of the former would be for the comic to at least entertain the idea that their sets always bomb because the jokes are shit. Your response (which amounts to "u just mad") indicates that you seem to have taken this approach.
You have made a fact claim. You said that K&R "doesn't even attempt" to cover undefined behavior. That claim has been disputed. The next step for you to do is not to change the subject by questioning the motives and emotional maturity of those who have disputed the claim, but rather to substantiate it.
(For the sake of completeness: K&R was neither my first introduction to C nor my first introduction to technical writing—not that it matters.)
> One can get a solid grounding in C reading Seacord's books and John Regehr's blog without even needing to know who K and R are.
I'm not familiar with John Regehr - At least recently, Regehr's blog appears to be extremely low level (relative to C at least). Reading about the IR in LLVM or the ARM machine code which makes a spinlock go isn't a "solid grounding in C". It's fascinating - but it's not about C. Perhaps he used to write more about C?
Robert Seacord though, I just don't see it. I think you've recommended Seacord because you're thinking about what you'd need to write good software in C in 2022. But if we wanted to write good software in 2022 we shouldn't use C for that task in the first place. That's my point here. "Secure Coding in C" goes on the shelf next to "Agile Development using the Waterfall Model" and "Version Control using Visual SourceSafe". In each case these should be one paragraph inside, "Better not I think".
My assumption is that the OP is curious about the language, to the extent maybe they'll solve Advent of Code in it or something, not that they're about to write or maintain serious C software.
Agreed. It's also worth pointing out that Effective C is the most current in terms of being up to date with present-day C standards.
K&R is so obsolete it's now wrong about some things. It's a pretty good read sometime down the road - when you can recognize its shortcomings - but it's not a book from which to start learning C.
K&R was a very interesting read for me, but not actually for the description of the C language, but because it describes why certain features in C look the way they do, and because it is also surprisingly and refreshingly critical towards the language (e.g. it basically admits that the function pointer syntax was a mistake).
It also corrected my oversimplified view that C is a simple and small language simply because it is an old language, but it was already a simple and small language compared to other languages in the late 60's and early 70's 'programming language zoo'.
All the code examples are deeply rooted in the 80's programmer mindset though, and are not very relevant for "modern C".
The above is a utterly clueless post and is best ignored.
To the OP:
K&R Ansi C is one of the best book to learn "Fluent C". The code is terse, no-frills and to-the-point. It is not a "Software Engineering" book so you have look elsewhere for that. Also without an understanding of the language, its runtime services and system interfaces (eg. ABI) it is pointless to look at Security/UB and hence don't bother about it in the beginning; You can get to it later.
In summary, read K&R C along with any other books that you like.
Well when K&R C was released the compiler landscape did look very different. I'm sure back then you could get away with a lot more UB than nowadays, where LLVM and GCC will use every millimetre of leeway you give them to break your programs.
The memetic nature of the term "K&R" also seems to have resulted in a lot of people who think Kernighan created Unix, and don't know who Ken Thompson is.
Peter A. Darnell and Philip E. Margolis C - A Software Engineering Approach (3rd ed.) https://www.amazon.com/Software-Engineering-Approach-Peter-D...
David Hanson C Interfaces and Implementations: Techniques for Creating Reusable Software (!st ed.) https://www.amazon.com/Interfaces-Implementations-Techniques...
The first is clearly written and focuses on ANSI C (lexis, syntax and semantics), with a healthy dose of software engineering education along the way, which you may already have. (It does not yet cover the latest ISO changes.)
The second book is a true gem that teaches even seasoned programmers how to implement correct, clean and portable components as libraries in ISO C, with plenty of source code to show how the true masters of the language craft the finest code you can in C. Most books how fragments of linked list implementations, but only this book shows complete a whole range of important ADTs you cannot live without (e.g. List, Ring, Atom) in C portably, with strong typing, bullet-proof error handling etc. (Hanson is also the author of lcc, a portable C compiler.)
I'm actually surprised that these are both are so little known, and that the second one hasn't seen more editions.