Hacker News new | ask | show | jobs
by sramsay 2298 days ago
It sounds like you're not asking, "How do I learn the language?" but "How do I know I'm doing it right?"

I think Gustedt's book is superb if you're just trying to learn the language, and it does a good job of presenting the most up-to-date standard. I admire the K&R as much as anyone else, but it's not exactly an up-to-date language resource at this point, and it doesn't really provide any guidance on the design and structure of systems in C (that's not it's purpose).

You might have a look at Hanson's C Interfaces and Implementations: Techniques for Creating Reusable Software. That's focused on large projects and APIs, but it will give you a good sense of the "cognitive style" of C.

21st Century C is very opinionated, and it spends a great deal of time talking about tooling, but overall, it's a pretty good orientation to the C ecosystem and more modern idioms and libraries.

I might also put in a plug for Reese's Understanding and Using C Pointers. That sounds a bit narrow, but it's really a book about how you handle -- and think about -- memory in C, and it can be very eye-opening (even for people with a lot of experience with the language).

C forces you to think about things that you seldom have to consider with Javascript, Python, or Go. And yes: it's an "unsafe" language that can set your hair on fire. But there are advantages to it as well. It's screaming-fast, the library ecosystem is absolutely gigantic, there are decades of others' wisdom and experience upon which to draw, it shows no signs of going away any time soon, and you'll have very little trouble keeping up with changes in the language.

It's also a language that you can actually hold in your head at one time, because there's very little sugar going on. It's certainly possible to write extremely obfuscated code, but in practice, I find that I'm only rarely baffled reading someone else's C code. If I am, it's usually because I don't understand the problem domain, not the language.

6 comments

Do any of these resources help with learning how to deal with:

1. The lack of abstractiom in C? 2. All of the pitfalls and gotchas: Undefined Behaviour, use after free, etc?

I've been writing Rust for a couple of years now, so I'm quite comfortable with low-level ideas, memory management, etc. But C still scares me because it seems so easy to make mistakes, and I also often find it hard to see the forest for the trees.

Perhaps C's just not for me?

I think both “Expert C” and “Modern C” do a reasonably good job working through memory management patterns, strategy, and constructs. However, I’m using this comment as an attempt discuss another topic you raised in your comment.

I don’t mean for this to be flame bait, but I do think it is worth pointing out and discussing if you desire. The concept of memory management that is presented in a majority of the Rust that I have seen written outside of deep library code and the parts of the low-level implementations of some standard library functions has very little relation to the analogous concepts in C. I’d say that what Rust considers memory management in average code is more similar to programming in C without using any heap allocation. I could be wrong or could just have not seen the right code bases, but there is very little C style byte level ‘data structure’ layout design and then the allocation of those sorts of data using multiple allocation strategies.

I certainly understand that the above mentioned constructs and patterns are really not part of Rust’s idiomatic usage, and that a chunk of C’s allocation options are semi-prohibited because of Rust’s memory model. But, if you are coming from Rust and going into C, the differences are far greater than the similarities in this area.

I’m certainly not questioning your credentials, experience, or ability, I really just feel like this area is lacking any substantial discussion where said discussion is not focused on the merits/drawbacks of the language design. You clearly know Rust and seem to be open to learning or working with C, so it isn’t about Rust v. C, it’s about compare and contrast, and highlighting the fundamental differences that could cause some mental roadblocks.

P.S. Sorry for rambling, Day Light Savings time is kicking my ass.

Not parent but Rust definitely makes you think about a lot of important aspects of memory management like lifetimes and references and ownership and stuff. In fact, it makes it impossible not to think about them: they're enforced by the compiler.

Those mechanics are pretty important but Rust definitely lets you program with values on the heap as well, it just does it in a strongly-typed fashion. I will agree that smart pointers are generally easier to work with, particularly with things like deref coercion, but Rust is philosophically closest to a language like C++ but with a stronger type system, memory safety, and lots of footguns removed.

I think Rust gives someone a better basis for learning and writing C than probably any other modern language.

I grokked C only after dabbling with Forth. C is too high level to know what's going on at the hardware level.
Most processors these days are basically running C virtual machines in hardware anyways.
> I could be wrong or could just have not seen the right code bases, but there is very little C style byte level ‘data structure’ layout design and then the allocation of those sorts of data using multiple allocation strategies.

My experience is mostly in very high level languages, C#, TypeScript, SQL, so that's probably reflected in the style of Rust code that I've been writing, but at least as one data point, yes, it is very possible to write day-to-day Rust code without getting into byte level data layout.

It does feel a lot like writing C code without using a heap. In fact the only case I've dealt with allocating uninitialised heap memory so far has been when interacting with a C API. Creating a new Vec does allocate memory on the heap (once values are pushed into it), but as a user it feels like passing around any other stack allocated struct. I think this is mostly due to the RIIA-style Drop and ownership system, as there is no extra code to free a value that owns a heap allocation vs a stack-only value.

The implementation of std::mem::drop is a fun example of this: https://doc.rust-lang.org/std/mem/fn.drop.html

Yes. Or, at least, the second part.

I'm not sure what you mean by "lack of abstraction in C."

"Abstraction" is not a straightforward property of a language; it's a thing you create with programming languages. It's true that some languages make it easy to express certain kinds of abstractions, but C has it's own way of abstracting things, and it's certainly not absolutely obvious that its way of doing so is inferior to others.

Sometimes making mistakes is a good way to learn, provided that you get feedback to help you identify them.

You can compensate for C's lack of compile time protections with run-time tools like valgrind [1] and the various sanitizers in clang [2, 3].

1: http://valgrind.org/

2: https://clang.llvm.org/docs/AddressSanitizer.html

3: https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html

There is an old but great book called "C traps and Pitfalls" on that subject.

Otherwise you're left alone with yourself and your tooling. Things like valgrind help a lot. But I'll admit that I too find large C projects alarming; it may be better to write "C with classes" code in a C++ environment, so at least you have classes and namespaces as an organisational tool.

I recommend Bradley's 'programming for engineers: a foundational approach to C and Matlab'. Simple book with relevant exercises.
If a book on C is described as "simple" I bet it's not about doing things right (which is what OP is worried about.) Doubly so if it covers something in addition to C.
Do you think that van der Linden's Expert C Programming is still worthwhile?

There's also the comp.lang.c FAQ http://c-faq.com/ , which is also very old now but still seems like a no-brainer to me.

I think Expert C Programming is a great book (really, as much a classic as the K&R), but I'm not sure I'd put it on the OP's list. At least not right now.

It says something about C (I'm not sure what) that it's possible to learn quite a bit about C from a book that was written in 1994. But it was written before C99, and it shows.

If you're a C hacker and you haven't read it, definitely do. It's great.

I second every book recommendation here. Another great one is The Standard C Library.
> the library ecosystem is absolutely gigantic

I can call out to C libraries from other languages, so that's not a big deal.

In addition, I think that probably the JVM ecosystem is larger and that Python may be as well.

You also find certain things at the bottom (like glibc, for example) where the C ecosystem isn't as diverse as you think it is.

For Linux userland, there are also musl and uClibc-ng. Other platforms have their own libcs.
Another great way to learn about C pointers is to reverse engineer games, live memory editing and scanning in particular requires a lot of global/heap pointer detective work.