Both r/cprogramming and r/C_programming are active, also full of lazy students trying to get people to do their homework. If you come by, describe your problem well with code. Say you're learning for yourself, not for school.
/* draws a point at 10, 5 */
struct point p;
p.x = 10;
p.y = 5;
draw(p);
I wish that would be a bit more 'modern', for instance:
struct point p = {
.x = 10,
.y = 5
};
draw(p);
...or even:
draw((struct point){ .x = 10, .y = 5 });
...main reason being that this avoids any accidents with unitialized data if the struct grows (the compound literal initialization makes sure that any struct members that are not mentioned are set to zero, while with the 'old school' approach you might end up with random junk in the struct).
+1 to https://hal.inria.fr/hal-02383654. It covers the language in extreme depth, and you likely won't remember it all on a first read but you'll walk away with a pretty good general understanding of the language. Plus, the author is an active contributor to the language.
I love C. As dangerous and as tedious it can be for large projects, I think it's my top choice for solo endeavors that don't require too many string manipulations. I don't think any language has come close to achieving the same balance of elegance and simplicity--maybe scheme? (zig comes close but has a substantial number of features that move it away from simplicity (e.g. payload capture))
I think Eskil Steenberg work in C is really top notch - the video is heavily recommended, as is an overview of his work in general, he does some awesome stuff with C.
A sibling comment already mentioned what it is so I'll not repeat that. Just some additional context if you're somewhat familiar with IRC. It's the spiritual successor of Freenode after it was taken over by a member of that community.
Not exactly your question, but valgrind is an amazing tool. It does execution-time analysis of your code and provides an in-depth report on memory usage. It will find and report memory leaks, use of uninitialized variables, bad memory references, etc. I love programming in C, but there are a lot of ways to corrupt memory, get segfaults, etc. valgrind is a useful and powerful way to guard against many of these kinds of problems. C++ is no better. The way I see it, programming in C is like lying on a bed of nails. Doable if you are very careful. With C++, they put a sheet over the bed of nails.
I haven't used valgrind in years, more used to asan, but I've been told it can do things asan can't?
Edit: to stretch the metaphor, modern c++ is like a thick rubber pad on the bed of nails, but often on the job you are hot bunking and you don't know which c++ your predecessor used to make the bed... Or you are in a hurry and you aren't sure which one you grabbed.
If you're used to ASAN, think of Valgrind like ASAN, TSAN, MSAN, and UBSAN all at once at the same time, and then has other facilities like profiling.
In any situation where I have control of how the code is built, I personally prefer to run those various SANs instead of Valgrind, usually three parallel builds: one with -fsanitize=address,integer,undefined, one -fsanitize=thread if more than one thread is involved in the project, and one -fsanitize=memory too if Linux is available. That gets you almost all the benefits of Valgrind faster than Valgrind.
But Valgrind is still an exceptionally powerful tool to learn and use. Its chief advantage to me is to be able to work with an unmodified binary. Its chief downside is it can be slow, and occasionally you may run into issues with support for more obscure instructions like vcvtps2ph.
MSAN is really the star of the sanitizer world, and is worth spinning up a Linux VM for. It tracks the initialized-ness of all your data and catches any use (really, branch) on anything uninitialized. I like to think of ASAN as "did I get my pointers right?" and MSAN as "did I get everything else right?".
If you're not familiar with integer sanitizer, it's like UBSAN except it checks for things that are often mistakes rather than things that are actually undefined. The most common situation I've found is code that's hashing data will often pop up a warning about unsigned integer overflow, which I choose to suppress as finely grained as possible with something like __attribute__((no_sanitize("unsigned-integer-overflow", "unsigned-shift-base"))). That way you can still find unintentional unsigned integer problems (e.g. with size_t).
Ideally you use both, as both can find some more obscure issues the other may not. At least in the past. Either in isolation also works well, though Valgrind is really a suite of many tools.
Don't bother with this stuff (or with "modern C") if you're just beginning to learn the language. You don't need it for small programs. No one is going to yell at you if your first effort isn't perfect. And no one is going to yell at you if you read the "wrong" book, or try things out in the "wrong" order. Pick any learning resource and get started now. You'll probably get distracted and move on to something else. Come back to it later or not.
Well C was my second language. I learned it on an amiga 500. But I quickly transitioned to a 386 (yes i know, a downgrade) but my dad put turbo c++ on it and I never wrote much ansi c after that. A bit for odd embedded targets, but mostly c++ my whole career. So I can write old fashioned c, and I don't start new projects in c, but writing c abi shims for c++ libraries or reading modern c is something I do with some frequency. Learning it would be fun maybe as I've never really used c99 or newer for anything.
Do bother with this stuff, it's super helpful and helps you understand your mistakes. Writing C without using sanitizers is like playing a game on hard mode, even for simple programs (especially when you're still learning).
You may leave static analyzers for later though, since they're more likely to bother you with unimportant stuff - but not much later.
Set warning level to max, use clang static analyzer or clang tidy (I'm not quite sure how those two relate to each other though), and especially ASAN (Address Sanitizer) and UBSAN (Undefined Behaviour Sanitizer) (and if you write threaded code: TSAN (ThreadSanitizer).
One thing that Apple did right in Xcode and where other IDEs lagged behind is easy access to those tools from the UI.
That sounds like all the things I do for c++. One thing I really like is that clang tidy can flag the "old way" to do stuff. Was hoping it had that for c.
One useful tool is running clang or gcc with —Werror -Wpedantic which will point out a lot of things which are useful to know about, even if occasionally a false positive.
It's great for what it is, but the language and style have come a long way since then. It would be better to learn "Modern C" (this is the title of a book but also at least learn C99 not K&R C).
E.g., people like the book by Jens Gustedt. I'm ambivalent about it, but it sure machine-guns the important points.
Not the person you are replying to, but here are my thoughts. It sounds like OP is looking to learn C for historical and contextual reasons, and for that K&R is perfect. The history of C is very K&R-centric.
For modern, practical C programing it is missing a lot though. It is a short, simple, from the original authors introduction to the language itself. There is nothing about how to test your code, how to set it up for long term maintainability, how to mitigate some of the security downfalls or even any of the features added since C89.
It's a good technical book. A great one for it's time and subject matter. But that caveat is important, it needs to be taken in context of the time. It shouldn't be the first, and absolutely not the only, book you read if you are writing modern production C in 2023.
> or how to mitigate some of the security downfalls.
I think I'd go further, there are places where it advocates downright unsafe practices. Eg. It's been a long time, but I thought I remember it introducing gets() unironically, without any caveat. For those that do not know, there is no safe use of that interface possible.
If you're just beginning with C, it doesn't matter. If anything, it's better to be exposed to these unsafe practices and make mistakes with them in the very safe environment of your own home, where absolutely no one cares what you're doing.
K&R C is an excellent learning resource primarily because it's extremely strong pedagogically. Read K&R and then move onto something else later when you're done with it. If you eventually get a job doing something with C, then you'll learn on the job what's important to the job you're doing.
I've taught C at university level, and used to give crash courses to new college grads and interns in industry.
The one thing I impart the most from the beginning is to develop good instincts for secure interfaces. You should be able to look at function signatures and see alarm bells for unsafe practices. For someone skilled at C this is second nature, you can see bad practices from a distance, often at the function signature level.
It's unwise to neglect it in the long run. It's perfectly reasonable to ignore it initially. There are different valid pedagogical approaches here. Your approach may be suitable for the type of student you were interacting with, but the profile you're familiar with doesn't represent every person who might be interested in learning C.
C is one of those things that unless you have proper testing, tooling and linting you don't know if you code is incorrect. It may have subtle memory bugs, rely on undefined behavior or crumple in exploitable ways under bad input. Having a feedback loop is really important in learning and improving. Undefined behavior helps subvert it.
For the exact example of above, how would a developer learn not to use gets? The best place would be a section on red flags in API design in the book. If you don't have that you need to find it through experience. Lints, fuzzing, sanitizers can help, but once again, none of this is covered in K&R. Some of it just didn't exist at the time or if it did, it was very primitive compared to what we have today.
Developers could pick up bad habits and be productive for years with them before they get bit by them. I know I did. I look back at some of the practices I picked up when first learning and cringe a bit. Especially since I think a fair amount of that code is still in production.
All of this is true, but if you are just starting with C, none of it matters.
If you continue using C, then you should eventually learn this stuff, but to overburden someone with a list of 100 things they must do right is the surest way to frustrate them to the point where they give up.
If OP eventually writes C code that goes into production for a company, it is the company's responsibility to have procedures and safeguards which ensure that OP writes reasonable code. Ideally, one such safeguard will be a senior colleague to introduce them to the thornier parts of C.
To expect a beginner to learn all of the finer points of a topic from the start is bad pedagogy.
On the other hand, K&R provides a set of exercises which are fun, graded, interesting, and challenging. By the time you finish them, you have a working knowledge of a large and useful subset of C. You can then proceed to reading another book (or consulting some other reference). Invariably, you will find that they disagree with K&R to some extent. Learning to spot these differences and begin to understand how C has evolved is itself an important lesson.
> If OP eventually writes C code that goes into production for a company, it is the company's responsibility to have procedures and safeguards which ensure that OP writes reasonable code. Ideally, one such safeguard will be a senior colleague to introduce them to the thornier parts of C.
You've mentioned something like this in reply to a few comments in here. This is a strange argument. It isn't too much to ask of a book to lay down more of a foundation so you don't need a 1 on 1 tutor later to correct misconceptions.
I get people like K&R because it is short and easy to read. Some subjects aren't best served by short and easy to read. The subject was well served by it when C was new and novel. We live in a very different world.
You don't actually seem to be addressing the quoted text... but I can reply to your arguments.
Sure, it would be ideal if there were a new book on C which did a better job of explaining some of the developments since K&R 2ed was written. If that book exists, I'm not aware of it. All of the books I've seen recommended are either too long, or poorly written. K&R's strong advantage isn't that it's short, it's that it's very strong pedagogically.
A "how do I learn C?" thread comes up on HN about once a month. In each of these threads, people will steer people clear of K&R, quoting something or another about how K&R isn't appropriate for this day and age (note: you do not explain why you think it isn't appropriate!). The tacit assumption here is that you can only read one book, or that if you read a book which isn't "perfect", you are somehow tainting yourself with corrupted knowledge. I think this is nonsense. An important meta-skill for learning is the ability to discern between good and bad information. The only way to build that skill is by consulting multiple sources.
Note that in my actual reply to OP, I don't specifically advocate for reading K&R, but instead to take a multipronged approach where you primarily draw from whatever learning medium you're most comfortable with. Most people I know that are in their early 20s or younger prefer learning from videos (I'm a math teacher and have lots of experience with people this age).
I think you've got a bit of a strawman in your head. Look back up at some of my comments you are replying to, namely the top one in this chain. A friend describes this as "violent agreement." I agree that it can be best to have a multipronged approach. That's why I said "It's a good technical book. A great one for it's time and subject matter. But that caveat is important, it needs to be taken in context of the time. It shouldn't be the first, and absolutely not the only, book you read if you are writing modern production C in 2023." I also address its short comings in that post.
Unfortunately, global hidden state is an unavoidable fact of programming. I don't see how one could be very good at programming if they were kept blissfully ignorant of it. Sometimes there is magic under there, and you just have to do your best to empirically figure out how that magic works.
libc has a few interfaces with implicit global storage. This leads to lack of thread safety and other issues. A good library will let the caller provide storage for these cases, or provide the caller a handle with which to pass it later/free it.
A trivial example would be strtok vs strtok_r. There are some, like, say, asctime(), that operate against a global string buffer.
Sometimes interfaces "look like" they're returning a global buffer, for example readdir(), but that structure can hang off the DIR* struct ... Nonetheless there is readdir_r()
Not being forced to declare symbols in the beginning of the scope is the real game changer I say. K&R or C89 without e.g. GNU extensions is really annoying to use.
Idk about that. I think it's code with everything declared at the beginning of the scope is easier to read and understand. Every time I see code written in this style (Git, Linux) it's easier to build a mental model of what is happening.
It's wonderfully written. I'm a big fan of both Dennis Ritchie and Brian Kernighan personally.
However the book was last updated in 1988 and I prefer C99+ for almost every feature it adds. If I'm going to recommend just one book it will be something newer.
If you have lots of time: https://hal.inria.fr/hal-02383654
If you can't be bothered reading a whole book: https://matt.sh/howto-c
Exercises: https://www.codestepbystep.com/problem/list/c and https://exercism.org/tracks/c
Once you have syntax and basic algorithms down well, watch this, the only 2 hour YouTube video I'll ever recommend: https://m.youtube.com/watch?v=443UNeGrFoM
Both r/cprogramming and r/C_programming are active, also full of lazy students trying to get people to do their homework. If you come by, describe your problem well with code. Say you're learning for yourself, not for school.
Together C & C++ is a good Discord if you prefer live chat: https://discord.gg/tccpp