Ask HN: How do I learn C properly? - <AUTHOR> I have 15+ years of experience in many languages: javascript, python, go, etc.
I can write C, but I'm not confident I'm doing it the right way. There are now decades of forums and blogs I can search through, but I'd love to have a single, clean, clear source of truth for recommendations on how to write C code.
I've started reading "Modern C" by Jens Gustedt. I'm not sure if this is the closest to a modern source of truth.
Note, I'd like my C code to work on Windows, which possibly restricts the "right way" to features provided by C89. But again, I'm not sure, the internet is super clear on this.
Thanks for the tips! <comment><USER6> 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.

<comment><USER7> 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?

<comment><USER8> 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.

<comment><USER9> 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.

<comment><USER10> I grokked C only after dabbling with Forth. C is too high level to know what's going on at the hardware level.

<comment><USER9> Most processors these days are basically running C virtual machines in hardware anyways.

</comment></comment></comment></comment></comment></comment><comment><USER11> The proper way to learn C is to get a good book, read it, and do exercises along the way.

Here's an excellent book: C Programming: A Modern Approach, by King.

Avoid online tutorials, and know that there are many poor books on C, including the recommended here "Learn C the Hard Way". It has factual problems; see under "Stuff that should be avoided" here: [http://iso-9899.info/wiki/Main_Page](http://iso-9899.info/wiki/Main_Page)

Note also, that unlike some other languages, C is not a language that you should learn by "trying things out", due to the nature of "undefined behavior".

Another recommendation would be to ask for CR on IRC.

Good luck!

<comment><USER12> > Note also, that unlike some other languages, C is not a language that you > should learn by "trying things out", due to the nature of "undefined > behavior".

I disagree. You have to learn the ways in which things fail in any language you learn, especially a language like C.

<comment><USER13> The point is that you don't learn all the ways in which things fail in C by trying. At best, you learn the way in which they fail (or fail to fail!) on your specific platform/compiler/version/compiler options combo.

If you want to know how things can fail (or, what things can fail in unspecified ways), you have to do a bit of reading and no amount of trying and experimenting will conjure that knowledge.

<comment><USER14> There isn't a guide to learn how things fail on platform, compiler, version, options combo. Probably the only things that force you to systematically research and accumulate such knowledge is by writing a language VM in C or use C as an intermediate representation. Even writing a C compiler itself won't teach you that.

<comment><USER13> > There isn't a guide to learn how things fail on platform, compiler, version, > options combo.

Yeah, that's the point. Chasing that knowledge is pointless. Instead, read the spec and find out what is legal and what is not. What is defined and what is not. Then you can stop worrying about how things fail and start worrying about not doing those things.

<comment><USER15> Trying c out with a known solid testing framework seems like a much more practical and useful way to start. Telling people to read a text wall before doing any practice at any task is a great setup for failure. Math doesn't do that. Physics doesn't do that. Chemistry doesn't do that. Literature doesn't do that. Philosophy doesn't do that. Etc. and of those philosophy is almost the study of text walls. But the best philosophy courses tend to start with a Socratic dialogue which is basically live action philosophizing.

<comment><USER16> Well, we also have lots of historic evidence (nearly every application written in C ever) that traditional/intuitive teaching methods _don 't work for C_.

All people have learned from the last 40 years of C is how to write ridiculously insecure applications.

Reading the standard may not be the optimal choice, or even the first choice, but common teaching methods are nearly guaranteed to produce crap results _in the specific case of C programming_.

<comment><USER17> I broadly agree. More so than any other other language, you cannot produce a good C (or C++) programmer by try-it-and-see alone. It's vital to have a grasp of the way the language is defined.

Point of disagreement: no amount of C expertise makes a hand-written C codebase safe. Vulnerabilities and undefined behaviour are often found even in code written by top-flight C programmers.

</comment></comment></comment></comment></comment></comment></comment></comment><comment><USER18> I work on Clang and the Linux kernel. JavaScript was the first language I feel I've mastered, before C. Of the many C books I have and have read, I only recommend 2:

Head First C by Griffiths and Griffiths

Expert C Programming: Deep C Secrets by van der Linden

The first is an excellent introduction, especially if you treat it as a workbook.

The second is a great intermediate book.

Really advanced stuff comes from seeing things in the wild.

<comment><USER18> Also, Jens' book is great, and I've learned from reading it, but it is way too long for a beginner.

</comment></comment><comment><USER19> An interesting way IME to learn C, is to code for an old platform like the Amiga.

All the same best practices apply, but the underlying OS is more simple and the constraints, like missing floating point, forces you investigate many aspects of C.

You can use Bebbo's m68k cross compiler to compile for the platform: [https://github.com/bebbo/amiga-gcc](https://github.com/bebbo/amiga-gcc)

No single source of truth for C best practices exist, but I can recommend using static analyzers like:

[https://splint.org/](https://splint.org/)

and

[http://cppcheck.sourceforge.net/](http://cppcheck.sourceforge.net/)

to steer you in the right direction.

Also, compile your code with the following gcc options: -pedantic -std=<c99 or c89> -ggdb3 -O0 -Wall -Wextra -Wformat=2 -Wmissing-include-dirs -Winit-self -Wswitch-default -Wswitch-enum -Wunused-parameter -Wfloat-equal -Wundef -Wshadow -Wlarger-than-1000 -Wunsafe-loop-optimizations -Wbad-function-cast -Wcast-qual -Wcast-align -Wconversion -Wlogical-op -Waggregate-return -Wstrict-prototypes -Wold-style-definition -Wmissing-prototypes -Wmissing- declarations -Wpacked -Wpadded -Wredundant-decls -Wnested-externs -Wunreachable-code -Winline -Winvalid-pch -Wvolatile-register-var -Wstrict- aliasing=2 -Wstrict-overflow=2 -Wtraditional-conversion -Wwrite-strings

Not that they all necessarily always makes sense, but they will reveal weak points in your code.

</comment><comment><USER20> Given the fact you already know some higher level languages I think the best way to learn C is to go low level.

C is fantastic language because it is within your reach to go and understand all small details of how various features are actually implemented. I find it very helpful to be able to understand how a piece of code actually functions on a low level.

Another tip: you are used to having high level features on your disposal. Don't try to duplicate those features. Figure out how actual C projects deal with program structure and flow issues that you are used to solving with high level constructs.

<comment><USER21> CPython is a particularly nice codebase to dig into. Plus, there's a big todo list: [https://bugs.python.org](https://bugs.python.org)

</comment></comment><comment><USER22> C is a very simple language. I have used just the official old book to create a compiler for it:

[https://www.amazon.com/Programming-Language-2nd-Brian- Kernig...](https://www.amazon.com/Programming-Language-2nd-Brian- Kernighan/dp/0131103628)

The best thing you can do is finding some open source code that interest you, read it and write it.

For example, I write my own controllers for CNCs, and there are lots of code for controlling stepper motors out there. I also learned OpenGL and sockets this way long time ago.

On the other hand, C is a terrible language for any big project unless you automate it some way.

I use mostly lisp in order to write C code automatically. C is too low level for writing any substantial code. Lisp has things like Macros(nothing to do with c macros) that can make you write code 10x, to 50x faster easily.

<comment><USER23> > _The best thing you can do is finding some open source code that interest > you, read it and write it._

Has reading source code in a language that's unfamiliar to you shown to be of any real benefit to learning? It seems you need at least a little bit of foundational experience with it before your brain can even parse what you're seeing in a beneficial way.

<comment><USER24> I'd tend to agree here also. I didn't start reading source code until a couple years in because I couldn't really put it to use. It becomes 10x more valuable when I was deep into my career learning from the big shots.

It _can_ help no doubt but not sure that's the best way to start. Especially with a language like C. It's not so straight forward without some training or CS education.

<comment><USER13> > It becomes 10x more valuable when I was deep into my career learning from > the big shots.

Yeah well OP said they've 15+ years of experience so..

> Especially with a language like C. It's not so straight forward without some > training or CS education.

I agree with pritovido, when they say C is a very simple language. Sure, it has grown its quirks and gotchas along with some cruft (the kind of things you wouldn't learn about in a CS curriculum anyway), but at its core it really is very simple.

</comment></comment></comment></comment><comment><USER25> Once you know the basics watch this: [https://www.youtube.com/watch?v=443UNeGrFoM](https://www.youtube.com/watch?v=443UNeGrFoM)

;-)

</comment><comment><USER26> In addition to the other recommendations, make sure you fully understand the memory model of your target architecture, the stack, the heap, the linkage.

Also, understand the undefined behavior, the compiler optimizations and how it can affect your code if you forget about the UB or if you ignore the compiler warnings, and read about the `volatile` keyword.

And a personal tip, please tell the compiler not to use strict aliasing (I think it's stupid) - you will get rid of some UB without much sacrifice.

<comment><USER11> Just to be clear, heap and stack are not part of C.

<comment><USER27> Then why does it make a difference whether you dereference a pointer to the stack or the heap at any point in your program?

The hardware representation of an int isn't part of C either but it most certainly had an effect on how your program will run.

<comment><USER13> > Then why does it make a difference whether you dereference a pointer to the > stack or the heap at any point in your program?

Ok, I'll bite. What difference does it make?

<comment><USER28> Because stack pointers are short lived, while heap pointers can be longer lived.

Say you have a queue of pointers that some thread is chewing on, you can't stick a pointer to a stack variable into that queue and then exit the scope of that stack variable, but you could put a longer lived heap pointer into that queue and exit the scope of the pointer, essentially passing ownership of the pointer to whatever's chewing on the other end of the queue.

<comment><USER29> The C standard does not talk about a heap or the stack; you could implement a "heap" by handing out pointers from the top level stack frame if you wanted to.

<comment><USER30> Is this important? By far the common case in C is using heap allocation as implemented by your libc or crt. It's pretty essential knowledge.

</comment></comment></comment></comment></comment></comment></comment><comment><USER31> Like anything else, set yourself a goal for a project. What kinds? C today is typically used for developing embedded systems, kernel internals, or device drivers.

Depending on how deep you want to go. The most in depth way to understand C is to learn assembly on an ISA first.

</comment><comment><USER32> If you're an experienced programmer, with some understanding of C, I recommend: "Expert C Programming: Deep Secrets" by Peter van der Linden.

It's one of the best books in the world. About anything. Ever.

<comment><USER33> I was going to post the same, now I don't have to, fabulous book.

</comment></comment><comment><USER34> > Note, I'd like my C code to work on Windows, which possibly restricts the > "right way" to features provided by C89

That might be the case if you plan to use the Microsoft C compiler as they have publicly said the don't have plans to update their C compiler.

However that is not your only option, since clang and several GCC ports will also work on Windows.

<comment><USER35> Are you not able to use their C++ compiler to get modern features? My understanding is that C++ is a superset and that a C++ compiler is also usually a C compiler. Is this view incorrect for modern implementations?

<comment><USER36> C++ is not a strict superset of C anymore. You can't compile all C in a C++ compiler.

<comment><USER37> has c++ ever been a strict superset of c?

afaik,

      int* arr = malloc(sizeof(int));

has never worked in c++.

<comment><USER36> It's probably never been a strict superset really.

</comment></comment></comment></comment></comment><comment><USER38> This is actually pretty hard. I recommend reading C codebases, but it can be hard to know which ones are good. Code to C99 for now; keep an eye on C11 etc. Have a personal library of C implementations of things. Don't worry about being on Windows, although if you're doing Windows development C#/.NET is the lowest friction path.

I'm writing a book on C. I don't think it will ever be finished or see publication. I recommend writing a book yourself - it's a great way to learn, and maybe you're a better writer than me and will publish it.

</comment><comment><USER39> It's hard to beat this list of quality books:

[http://iso-9899.info/wiki/Books#Learning_C](http://iso-9899.info/wiki/Books#Learning_C)

Read some and do the exercises. You'll learn to write portable code to a standard rather than stuff that "accidentally works." There's a lot of crap C code in the world, full of GNUisms, overzealous macros, header-only nonsense abusing translation units, and copypasta. Don't pick up bad habits from that stuff.

</comment><comment><USER40> Learn (a little bit of) assembly first. It will make more sense IMO if you came at it from below rather than above, so to speak.

</comment><comment><USER41> I would recommend "Learn C the Hard Way" by Zed Shaw to get you started for something practical, because it goes over how C is written in the real world. Of course, situations will vary. The original book about C by K&R is an excellent book too.

<comment><USER42> Zed makes it clear in that book that he hates C and doesn't use it anymore due to its undefined behaviour.

<comment><USER29> Zed isn't actually very good at understanding undefined behavior and has demonstrated this loudly and repeatedly. I would steer clear of his book, personally.

</comment></comment></comment><comment><USER43> There is no clear source of truth for C. Depending on what you want to do there is quite different approaches. C for embedded systems tends to look quite different from C for systems programming. For instance MISRA is a C coding standard for embedded devices in Motor Vehicles (but many of its ideas are ideas from writing robust embedded systems), it says don't use things like dynamic memory. You'd never use that if you are writing software for linux/windows. So learning C "properly" would be about learning the different approaches people use in C to write robust code and what tradeoffs they are making. Understanding the C standard and the variations. Understanding bare metal programming. Understanding the effect of different underlying processors and hardware architectures. Understanding systems programming and how to consume APIs and how to create reusable binary modules. Understanding the compiler/build system the intermediate files / code generation / memory layout. Read a variety of books ( including K&R ) and study code ( Luas implementation is some very nicely written C )

</comment><comment><USER44> I liked "C Programming: A Modern Approach"[1]. It has been several years but as I recall it was well suited for self study and was pretty explicit in calling out places it was talking about C99 as opposed to C89.

[1]: [https://www.amazon.com/C-Programming-Modern- Approach-2nd/dp/...](https://www.amazon.com/C-Programming-Modern- Approach-2nd/dp/0393979504)

<comment><USER44> You might also take a look at this Minecraft clone in C[1]. Uses sqlite to persist state so you can see how you might interact with a database as well. Quite modern and readable in that most functions outside main.c are <20 lines long. It's also cross platform and should work on Windows, Mac and Linux.

[1]: [https://github.com/fogleman/Craft](https://github.com/fogleman/Craft)

</comment></comment><comment><USER45> Essential reading:

[https://blog.regehr.org/archives/213](https://blog.regehr.org/archives/213)

[http://blog.llvm.org/2011/05/what-every-c-programmer- should-...](http://blog.llvm.org/2011/05/what-every-c-programmer-should- know.html?m=1)

<comment><USER46> omg no. Stop obsessing about this dragon. I'm not sure I've ever met it in my life (you know, other than segfaults from NULL dereferences which are UB too).

<comment><USER29> How much C have you written, and how much has gone through a high-performance optimizing compiler?

<comment><USER46> I don't know. Quite a bit - maybe written 300K lines and deleted > 200K lines. Most of it was compiled by gcc -O2 and/or MSVC. I've never relied on vectorization or used a lot of intrinsics, though.

<comment><USER29> I'm honestly quite surprised you've _never_ run into surprising behavior caused by the optimizer handling undefined behavior…

<comment><USER46> Not that I'm aware of it.

</comment></comment></comment></comment></comment></comment><comment><USER47> The Visual Studio C compiler (not the C++ compiler) supports "almost all" of C99, I would definitely recommend skipping C89, and use at least the new initialization features from C99 because they can reduce bugs caused by uninitialized data. Also, C++ compilers support a subset C99 that's somewhere around "C95" and is already a much friendlier version compared to C89.

TBH, I think the best way to learn C is to tinker a bit with assembly first (it doesn't have to be a modern assembly dialect, just write a few 6502 or Z80 assembly programs in a home computer emulator). Coming from "below" instead of "above" C's abstraction level helps understanding the design rationale behind C, and the trade-offs between flexibility and unsafe memory handling.

</comment><comment><USER24> I learned a few basic levels of C using my subscription to pluralsight.com.

But... I quickly switched to Rust as I see it being the C of the future as it continues to develop. That's just me though. (I have similar opinions towards things like Kotlin or Go.)

The books I am reading in the comments below are great resources.

</comment><comment><USER48> With C++, going modern-first is a great approach. But C hasn't really been modernized that much, and because Microsoft has been so slow to fully support even just C99, let alone C11 or any GCC/clang extensions, often one has to to stick to a subset of C99 anyways -- exactly as you surmised.

The problem with C is that it's extremely dangerous. Besides learning how to program in C, you'll really want to know how to use memory debuggers such as valgrind and Dr. Memory ([https://www.drmemory.org/](https://www.drmemory.org/)), as well as AFL for fuzzing, and various GCC and clang sanitizers. You'll really want to let functional programming language experience inform your approach to C API design and implementation.

<comment><USER49> Microsoft hasn't been slow, they have been quite clear, C is legacy on Windows, given its security implications, all new code should be C++ and the compiler is called Visual C++ after all.

C support is done to the extent of ISO C++ requirements.

For those that still want to use plain old C on Windows, they have contributed to clang, and it is now available as part of Visual Studio installer.

</comment></comment><comment><USER50> The most important difference between a higher level language and C is that you really _have_ to understand a bit about the underlying hardware architecture to do it "right". C is a fairly thin veneer over assembly and it helps to have that mental model in mind when working in it.

It's a very small language with almost no batteries included, built very much around the manipulation of pointers and memory more than just about anything else.

To do C the "right" way means approaching problems from that perspective and building mountains out of toothpicks while thinking about how the billions of toothpicks need to interact with one another so they don't crash/catch fire/etc.

</comment><comment><USER51> I'd just type in the examples from K+R, try them out, and modify them to do new things.

<comment><USER52> K+R is pretty ancient.

<comment><USER29> That doesn't make it worthless; it's still probably the best book to learn C today if supplemented correctly.

</comment></comment></comment><comment><USER53> Becoming proficient in a language usually involves: \- writing code with it long enough, \- collaborate in a project based on it, \- reading code other people wrote, \- keep yourself up to date by following the language evolution, \- go more in depth in language internals, compilers, etc...

I wrote C for 10+ years (mostly bare metal FW), yet I am still amazed of how little I know about it. Recently for example I learnt of all the things the dynamic linker does in Linux, how symbols memory addresses are resolved at runtime using PLT, ....

The good point about C is that it can be used for very different kind of projects, so the choices are a lot.

<comment><USER54> This was a pure satisfaction when I have first seen it :)

[https://en.wikipedia.org/wiki/Duff%27s_device](https://en.wikipedia.org/wiki/Duff%27s_device)

<comment><USER29> (Please don't write code like this anymore)

<comment><USER54> Sure, duff device is obsolete due to cpu zero overhead looping but it is still nice to see, mind boggling piece of code. :)

</comment></comment></comment></comment><comment><USER55> The C FAQ used to be a good place to start. Note, _used_ to be:

[http://c-faq.com/](http://c-faq.com/)

What did you want to do on Windows? I think this depends as much on the compiler as the code.

<comment><USER30> Second this. Read it all more than once; the insights it will give you into C are so valuable.

I'll also recommend C++ FQAs [1]. It's C++, yeah, but it's relevant to C in many places, and since a lot of systems programming now uses C++ as the lingua franca, it's a good, entertaining resource.

1: [http://yosefk.com/c++fqa/](http://yosefk.com/c++fqa/)

</comment></comment><comment><USER56> I would read K&R, it's a right of passage. Look at some of the larger open source projects for how to organize and manage a large c project.

And practice pointers. Get really comfortable with how memory works.

</comment><comment><USER57> I'm currently learning C myself and something that I realized is that often the hard part to learn is not C the language but the domain in which is being used.

For example, I'm trying to learn Linux drivers or embedded programming and I thought that I'd find in a C book information about registers and such, but registers don't really belong to the C domain but to the hardware domain.

Thus, maybe find a way to clarify what you want to use C for and then learn those domain problems and how C solves them.

</comment><comment><USER58> Programming in C requires you to take proper responsibility for what you write, but also allows for great power in return.

I've been working on a reasonably large (and cross platform) project in C ([https://domeengine.com](https://domeengine.com)) for a couple years now, and I find that C generally requires a certain kind of discipline. When you interact with an API, you need to read the docs, figure out their specific preconditions, and requirements on handling frees, establishing what you own and what you don't.

It also helps to make sure that if you allocate something, you clean up after in a clear and well defined way.

You should also get very familiar with debuggers (LLDB/GDB) and other dev tools, to help when you have made a mistake.

In the modern era, you have a couple of options for getting it to run on Windows. C89 using Visual Studio, The MSYS2/MinGW2 gcc toolchain, or a gcc toolchain using the WSL. I use the MSYS2 tools for DOME because it requires the fewest platform-specific modifications to my code, but this varies by usecase.

</comment><comment><USER59> Pretty obvious answer, but for those who are new to C, the C Programming Language by Brian Kernighan and Dennis Ritchie is a good place to start

<comment><USER29> Best paired with a bunch of modern resources about how undefined behavior works.

</comment></comment><comment><USER60> An important question to ask yourself is why do you want to learn C? If you want to be able to read large C codebases makes sense. But if you want to write embedded software or systems software, rust or c++11 (ugh) are arguably a better place to start. I say this as someone who has learned C but moved on to c++ without regrets. Though I have serious rust envy :)

<comment><USER61> What is stopping you from getting into Rust?

<comment><USER60> My current work projects require c++ unfortunately. Some day. Sigh

</comment></comment></comment><comment><USER62> while not a definitive guide, [https://matt.sh/howto-c](https://matt.sh/howto-c) is a good example of how to approach "modern" c.

c99 is fully implemented in windows with msvc (2015), gcc, clang, and intel's compiler, so "right way" should not need to involve c89. most are c11 compliant as well.

</comment><comment><USER63> If you're into visual/interactive learning, please give Harvard's CS50 a try.

[https://www.youtube.com/channel/UCcabW7890RKJzL968QWEykA](https://www.youtube.com/channel/UCcabW7890RKJzL968QWEykA) [https://cs50.harvard.edu](https://cs50.harvard.edu)

</comment><comment><USER64> First step is making sure you're using the flags

`-Wall -Werror -pedantic` and then one of `-std=c89` or `-std=c99`

(Or equivalent in whatever Windows C compiler)

<comment><USER65> Why would you suggest older versions?

</comment></comment><comment><USER66> I've written many tens of thousands of lines of C, but retired 15 years ago. The two best books I know are:

1\. C: A Reference Manual, by Harbison and Steele. [https://www.amazon.com/Reference-Manual-Samuel-P- Harbison/dp...](https://www.amazon.com/Reference-Manual-Samuel-P- Harbison/dp/013089592X/ref=dp_ob_title_bk)

2\. The C Puzzle Book, by Alan Fueur. [https://www.amazon.com/Puzzle-Book- Alan-R-Feuer/dp/020160461...](https://www.amazon.com/Puzzle-Book-Alan-R- Feuer/dp/0201604612)

Harbison and Steele has much better explanations than K&R. The Fueur book taught me a lot about C declarations. Declarations are that part of C language that is them most unnecessarily difficult.

You asked about a slightly different question, best practices. But in the real world you'll run into a lot of code that practices below that level.

</comment><comment><USER67> I would focus on sources that use C11 or C18, since there are some niceties in the newer standards.

As a reference, I like QEMU's source code[1]. It's huge, but the style and practices found in any file will help you get a grip on good C.

[1] [https://github.com/qemu/qemu](https://github.com/qemu/qemu)

</comment><comment><USER68> This is a fun book to start with "Expert C Programming Pete van Linden" [https://www.amazon.com/Expert-Programming-Peter-van- Linden/d...](https://www.amazon.com/Expert-Programming-Peter-van- Linden/dp/0131774298)

</comment><comment><USER69> In Modern C (book mentioned by OP), there is takeaway 184.108.40.206 which says "Don't use NULL" because:

The definition in the C standard of a possible expansion of the macro NULL is quite loose; it just has to be a null pointer constant. Therefore, a C compiler could choose any of the following for it: 0U, 0, '\0', 0UL, 0L, 0ULL, 0LL, or (void * )0. It is important that the type behind NULL is not prescribed by the C standard. Often, people use it to emphasize that they are talking about a pointer constant, which it simply isn't on many platforms. Using NULL in a context that we have not mastered completely is even dangerous. This will in particular appear in the context of functions with a variable number of arguments.

NULL hides more than it clarifies. Either use 0 or, if you really want to emphasize that the value is a pointer, use the magic token sequence (void * )0 directly.

[https://www.gnu.org/software/libc/manual/html_node/Null- Poin...](https://www.gnu.org/software/libc/manual/html_node/Null-Pointer- Constant.html) says:

The preferred way to write a null pointer constant is with NULL. You can also use 0 or (void * )0 as a null pointer constant, but using NULL is cleaner because it makes the purpose of the constant more evident.

If you use the null pointer constant as a function argument, then for complete portability you should make sure that the function has a prototype declaration. Otherwise, if the target machine has two different pointer representations, the compiler won't know which representation to use for that argument. You can avoid the problem by explicitly casting the constant to the proper pointer type, but we recommend instead adding a prototype for the function you are calling.

[https://man.openbsd.org/style](https://man.openbsd.org/style) says:

NULL is the preferred null pointer constant. Use NULL instead of (type * )0 or (type * )NULL in all cases except for arguments to variadic functions where the compiler does not know the type.

\---

Readers: what is your take on it and why?

<comment><USER13> > Readers: what is your take on it and why?

It's ok to draw a line on what implementations you care about. And honestly, at this day and age, I couldn't care less about implementations that define NULL to be 0 (or something else that isn't a pointer). It's a case of where it's not worth everyone's time to be catering for the lowest common denominator.. instead, fix that implementation or just go download a better one.

If I could make breaking changes to C today, I would remove the NULL macro and replace with a lowercase null (or nil or whatever) keyword a definition that doesn't impose pointless burden on its users.

That said, I'm still very much in the habit of casting NULL to void* when I'm using it as an argument for variadic functions. It's silly, but that's just how we do things...

<comment><USER70> You could make a proposal to the C committee to add a keyword `_Nullptr` and to add a header `stdnull.h` which has `#define nullptr _Nullptr`, like they did with bool.

<comment><USER13> Yep. I'd be happier to work with the C committee if they were a little more open. For example, their mailing list could be public.

</comment></comment></comment></comment><comment><USER71> I am currently relearning C, I tried different books over the years (to not say decades), never did more than a toy project. The book I picked up this time was extreme c (from Amazon reviews, failry new book), by Kamran Amini, and so far (few chapters in) it's fairly enjoyable as it takes C, compilation process etc from the "basics", fairly hands-on and not too much theorical and relatively fast paced. I feel that the book is more about how to make a program C and not teaching literally the language itself. So really what I needed.

</comment><comment><USER72> I think you can use any version of Visual studio to write C programs. I recently did a video tutorial using Visual Studio: [https://www.youtube.com/watch?v=JaDzgmZvY00](https://www.youtube.com/watch?v=JaDzgmZvY00) on the "C++ Modulus Operator" [https://www.mycplus.com/tutorials/cplusplus- programming-tuto...](https://www.mycplus.com/tutorials/cplusplus-programming- tutorials/the-c-modulus-operator/)

</comment><comment><USER73> Essential parts of C language are actually really a small set.

What I'd _suggest_ is, learn syntax and try to understand linux kernel (which is one of the very well crafted software piece we have written in C). If you don't want to go that much of a deep dive, you can check sqlite source code as well. Writing code starts with reading it. Do yourself a favour and spend more time on reading code than reading books.

</comment><comment><USER42> Perhaps better than a book, can anyone authoritatively point to a few small- and-readable but best-in-class open-source C projects to use as a reference?

<comment><USER74> Lua and Sqlite sources are excellent.

<comment><USER75> Linux as well. The comment culture of C devs really help with understanding all of it.

<comment><USER76> There was a book featured in a HN post about a year ago - an older Linux source with extra commentary, used by students in China, I think (book is in English)

</comment></comment></comment></comment><comment><USER77> All the advice already here is very good and implementable.

Here are my two cents.

I have been programming in C professionally for the past 12 years, and I think the way to go would be to go implement a well known tool in C and then compare your code with the open source code of that tool. e.g. Git.

You will learn a lot from how the feature that you chose to implement in a certain way was implemented in a completely different way for various reasons.

</comment><comment><USER78> It's not easy. The obvious answer is read and write lots of code. Experiment. Make sure you actually know how a computer works. Learning some assembly even if you don't ever use it can help with that (free ebook "Programming from the Ground up" can be done in a weekend) Read existing code bases, different styles, like linux or some GNOME stuff.

</comment><comment><USER79> Finding "the right way" is closer to religion than it is to problem solving and science.

You will always be vulnerable to criticism and other people's opinions, and you need to open up and acknowledge this fact and work with that. There are best practices and principles you can adhere to. Stand up for your design decisions, and refactor when you have to.

</comment><comment><USER80> I'd say there's no proper way. Every project has its way of writing C. With the use of macros, sometimes it looks a bit like a DSL for every specific project.

My advice: think about what kind of software you want to write and look for similar projects, libraries, or contribute with a new module, functionality, adapt something to your needs...

<comment><USER22> I would advice AVOID C macros at all cost. They are so weak and hard to debug and problematic.

I write in several languages and read lots of C projects made by others and find the affirmation hard to believe: C is so simple that is very easy to read if the writer of code has a minimum of competency.

You can say that of projects like C++, specially things like C++11 that are languages by committee so complex that you can affirm that every C++ programmer is different or find programmers that could not understand each coder's code. Not so with C.

That happens to me with C++ and lisp code. It takes a while for me to understand what the author uses(or abuses) before being able to understand the code. That does not happen with C code.

<comment><USER70> The Linux kernel uses lots of macros, many of them with lowercase names that are barely distinguishable from function names. Given the quality of that codebase, I think not using macros is a bit silly.

</comment></comment></comment><comment><USER81> Get code reviews from someone that actually knows C. Mentorship and harsh code reviews are the best way to learn.

</comment><comment><USER82> I use C since 1992, but I never confident in my C code, unless it heavy with macros to check error codes and return early, register memory allocations and free memory at return, and so on. Rust improved my confidence in C code a lot, but it looks more like Rust code now.

</comment><comment><USER83> In my opinion, one of the main reasons C looks old and not practical to beginners is the standard libs API.

But it is perfectly possible to use C as a language with a more modern and easier to read API, but you'll probably have to build your own.

</comment><comment><USER84> There are other compilers for windows than MSVC++, so you can move beyond C89 if you wish.

stdint.h is useful and should be required reading. I still come across too many C projects that reimplement it poorly.

Learn to use valgrind, and maybe ddd too.

</comment><comment><USER85> You can compile with clang on Windows, so you can use later standards.

</comment><comment><USER86> The main thing that C provides over other languages is direct hardware access. So study the hardware, find out what you want to do with it, then use C to implement it.

</comment><comment><USER87> Besides all that is mentioned already to read or do. I would suggest to try writing something you coded in python or go previously in C.

</comment><comment><USER88> Read John Regehr's blog, eg [https://blog.regehr.org/archives/213](https://blog.regehr.org/archives/213)

Read "Who Says C is simple?": [http://cil- project.github.io/cil/doc/html/cil/](http://cil- project.github.io/cil/doc/html/cil/)

Take up a different language :-)

</comment><comment><USER89> To start with, "The C Programming Langauge", Kernighan & Richie (K & R) [1]. Find the latest edition that you can buy.

I think the primary topic to master in C is pointers. This is where most falter. It takes a few years to "master" (if we ever do). Here I would recommend "Understanding and Using C Pointers", Richard Reese. [2]

If you are interested in networking, any of the classic "TCP/IP Illustrated Vols I/II/III", W. Richard Stevens, [3] contain a ton of C code to implement components of TCP/IP.

If you are interested in Graphics, then "Graphics Gems", Andrew Glassner [4] is a good source.

"An Introduction to GCC", Brian Gough, [5] to understand the tooling and its various bells and whistles.

My learning swimming by jumping into the deep end of the pool experience was realized by learning Windows Programming using the Charles Petzold book and navigating through Microsoft Foundation Classes in the late 80s/early 90s. The state of the art in tooling wasn't that great in those days and I spent months with the book to get things going. This was done after I had built a foundation with K&R and a decent amount of Unix network programming.

I see a lot of the other posts recommend more modern books. But you still need to build your foundation on C and Pointers in particular.

Good luck on your journey.

[1] [https://www.amazon.com/Programming-Language-2nd-Brian- Kernig...](https://www.amazon.com/Programming-Language-2nd-Brian- Kernighan/dp/0131103628/ref=sr_1_1?crid=2FEGL0ITD7BO&keywords=the+c+programming+language&qid=1583705302&sprefix=The+C+Progra%2Caps%2C212&sr=8-1)

[2] [https://www.amazon.com/Understanding-Using-Pointers- Techniqu...](https://www.amazon.com/Understanding-Using-Pointers-Techniques- Management/dp/1449344186/ref=sr_1_2?crid=32I1T4I421WPZ&keywords=understanding+and+using+c+pointers&qid=1583705390&sprefix=Understanding+and+Using+C+P%2Caps%2C221&sr=8-2)

[3] [https://www.amazon.com/TCP-Illustrated-Protocols-Addison- Wes...](https://www.amazon.com/TCP-Illustrated-Protocols-Addison-Wesley- Professional/dp/0321336313/ref=sr_1_1?crid=V0LZ9TYHEOFS&keywords=tcp%2Fip+illustrated&qid=1583705448&sprefix=TCP%2Fip+%2Caps%2C222&sr=8-1)

[4] [https://www.amazon.com/Graphics-Gems-Andrew-S- Glassner/dp/01...](https://www.amazon.com/Graphics-Gems-Andrew-S- Glassner/dp/0122861663/ref=sr_1_1?crid=1WEA9UYLALBRE&keywords=graphics+gems&qid=1583705599&sprefix=Graphics+Ge%2Caps%2C212&sr=8-1)

[5] [https://www.amazon.com/Introduction-GCC-GNU- Compilers/dp/095...](https://www.amazon.com/Introduction-GCC-GNU- Compilers/dp/0954161793/ref=sr_1_1?crid=1L8K7ERCL07RZ&keywords=an+introduction+to+gcc&qid=1583705516&sprefix=introduction+to+GCC%2Caps%2C218&sr=8-1)

</comment><comment><USER90> I am in the same boat as you are...bare with me by no means I am an expert.

In fact, I read couple of chapters in Modern C yesterday :). Here are some of the things I am doing to improve my C skills to match with some of the admired/professional developers.

Decide which platform to use

<comment><comment><comment><comment><comment><comment>

Unfortunately, to become proficient in it we need to write code and focus on a platform. I have been fighting between whether to develop on Windows vs Linux. I am very experienced in Windows environment(using debuggers/cl/linkers/Windbg etc) but when it comes to writing good quality C code(not C++) and for learning how to write good maintainable moderately large source code, my research showed that Windows compilers/C standard APIs are not great, in fact they hinder your productivity. I have wasted countless number of hours to just figure out how to properly create a simple C project with a decent build system. Unfortunately, I could not find one. The closest I could find is CMake as MSBuild is a nightmare to work with. I even tried NMAKE but failed. When it comes to documentation of finding basic C Api usage, MSDN([https://docs.microsoft.com/en-us/cpp/c-runtime- library/run-t...](https://docs.microsoft.com/en-us/cpp/c-runtime-library/run- time-routines-by-category)) does a decent job. But in the name of security you will find zillion variations( __s,_ _l) of an API and by default msvc compiler will not let you use some of the API in their simple forms. Instead, you have to define _CRT_SECURE_NO_WARNINGS etc. I think for someone just getting started to develop/learn to write a decent code base in C these restrictions really hinder the productivity. So finally, I have decided to instead focus my learning on Linux platform(currently through WSL - Windows subsystem for Linux) with its POSIX apis. You know what, `man 3 printf` or `man 3 strlen` is soooooo much better than googling msdn

Mastering C

<comment><comment>~

I think, the simple and straight answer here is reading good code and writing "good" code and also reading good C content(be it books or articles). I think these are the three ingredients necessary to get started. Of all the open source projects that I have investigated, I found Linux Kernel and related projects seems to have very good taste in terms of code quality. Currently, I am just focused how they use the language rather than what they actually do in the project. Things like, how they structure the project, how they name things, how they use types, how they create structures, how they pass structures to function, how they use light weight object based constructs, how they handle errors in function(for example forward only goto exits), how they use signed/unsigned variables etc(more of my learnings to the end), how they use their own data structures. I think its good to initially focus/target on ANSI C API with C99 instead of heavily relying on the OS specific API on which ever platform you choose. For example, such projects could be writing binary file parsers for example projects like .ISO file format etc.

Good C projects/articles

<comment><comment><comment><comment><comment>

1\. Winlib.net - [https://github.com/jcpowermac/wimlib](https://github.com/jcpowermac/wimlib) is a great source of information

2\. e2fsprogs - [https://git.kernel.org/pub/scm/fs/ext2/e2fsprogs.git/](https://git.kernel.org/pub/scm/fs/ext2/e2fsprogs.git/)

3\. MUSL - [https://git.musl-libc.org/cgit/musl/tree/](https://git.musl- libc.org/cgit/musl/tree/)

4\. General C Coding Style - [https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/lin...](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/process/coding- style.rst)

4\. [https://nullprogram.com/tags/c/](https://nullprogram.com/tags/c/) \- great source of C knowledge

5\. CCAN - [https://github.com/rustyrussell/ccan/tree/master/ccan](https://github.com/rustyrussell/ccan/tree/master/ccan) \- great source of C tidbits from none other than Rusty Russell - I haven't read all of them

6\. POSIX 2018 standard - [https://pubs.opengroup.org/onlinepubs/9699919799.2018edition...](https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/)

continued in the comment....

<comment><USER90> My Learnings(know your language/know your complier/know your tools)

<comment><comment><comment><comment><comment><comment><comment><comment><comment><comment><comment><comment><comment><comment><comment>~

1\. _t suffix is the notion used to denote a typedef for a given type /* This example is from do_journal.c in e2fsprogs _/ struct journal_transaction_s { ... blk64_t start, end; ... }; typedef struct journal_transaction_s journal_transaction_t;

2\. Know about headers like stddef.h and stdint.h and when they are supposed to be used. for example: When to use normal data types like int vs int16_t etc.

3\. From [https://en.cppreference.com/w/c/types/integer](https://en.cppreference.com/w/c/types/integer) we can sense that int_t_ are of exact width types which might have some perf side effects if the underlying hardware does not support the width natively.

        For example, in visual studio x86/x64 we have typedef short int16_t; and
        typedef int int32_t; int_fast_t* on the other hand make sure a suitable
        width which maps natively to the available hardware type For example, in
        visual studio x86/x64 typedef int int_fast16_t; instead of typedef short
        int_fast16_t; size_t on the other hand alias to the natural unsigned word
        length of the hardware for example on x86 typedef unsigned int size_t; and
        on x64 typedef unsigned __int64 size_t;

4\. Know your compiler predefined standard macros On Microsoft compiler _WIN64 - defined when we are compiling for x64 code _WIN32 - defined when both x86 and x64 code is getting compiled _MSC_VER - defines which compiler version are we using, indicate different visual studio versions __cplusplus - defined when the translation unit is compiled as C++

5\. We can get a FILE* from HANDLE using below APIs from io.h and fcntl.h Fd = _open_osfhandle((intptr_t)Handle, _O_TEXT); File = _wfdopen(Fd, L"r"); Once we get the FILE* we can use fgets for line oriented string operations

6\. Learned about var args and aligned memory Aligned memory means the address returned by the _aligned_malloc is always divisible by the alignment we specify. For example: char _p = _aligned_malloc(10, 4); the address return in p will be always be divisible by 4. We should also free the allocated memory using _aligned_free(p)

7\. atoi(str) this api also processes input string until it can convert. For example atoi("123asda") will still give 123 as the return result. Any whitespace in the beginning of the input string will be ignored. So atoi(" 123asd") will still return 123. It is recommended to use strto_ functions to convert strings to int/long/float types as they also can return pointer to the character which is a non integer

8\. UCRT support around 40 POSIX system level APIs but most of these have _ prefix to them. wimlib in wimlib_tchar.h defines #define topen _open for Win32 and #define topen open for POSIX systems The take away here is the UCRT implementation even though differ in name the parameters are exactly the same.

        For example:
          UCRT Win32: int _open(const char *filename, int oflag, int pmode);
          POSIX:      int  open(const char *pathname, int flags, mode_t mode);

9\. We can install only build tools(VC compiler) excluding IDE from [https://aka.ms/buildtools](https://aka.ms/buildtools)

10\. Best video on C Standard and some of its less known features - "New" Features in C - Dan Saks Year C Standard Comments 1983 C standard committee is formed 1989 C89 C89 US standard 1990 C90 C89 International Standard 1999 C99 C99 Standard 2011 C11 C11 Standard 2018 C18 C18 Bugfix release

        _reserved - Reserved for global scope. But we can use any identifier with an
        _ as a local variable or a structure member

        __reserved - Always reserved. Meaning the user program should not use any
        variable with two underscores __

        _Reserved - Always reserved. Meaning the user program should not use any
        variable with an underscore and capital letter.

        This is the reason why _Bool is named that way to prevent breaking existing
        bool typedef used in existing code.

11\. Another good video on lesser known C features - Choosing the Right Integer Types in C and C++ - Dan Saks - code::dive 2018

        we can use CHAR_BIT from limits.h instead of 8 for example when you want to
        print the bits in a integer, we can do below `for (size_t i = sizeof(int) *
        CHAR_BIT; i >= 0; i--) {...}`

12\. size_t denotes the native architecture supported natural word size. So for 32bit it is 4 bytes unsigned quantity and for 64bit it is 8 bytes unsigned quantity. Hence it is defined as follows

        #ifdef _WIN64
            typedef unsigned __int64 size_t;   //8 bytes on x64
        #else
            typedef unsigned int     size_t;   //4 bytes on x86
        #endif

        where as uintmax_t denotes the maximum integer type that is available in the
        language. So on a 32bit you could still represent a 64 bit quantity using
        long long even though it not what the architecture directly maps to. So
        below is how it is defined in both x86 and x64

        typedef unsigned long long uintmax_t;  //in MSVC both x86 and x64 support 64
        bit quantities using long long. So size_t does not give us the maximum
        unsigned integer, instead it gives us the native unsigned integer i.e., on
        x86 it will be 32bits and on x64 it is 64bits. So recommendation is to use
        size_t where ever possible instead of using int. for example.

        int len = strlen(str); // not recommended because on both x86 and x64 of MSVC int is 4 bytes due to LLP64
        size_t len = strlen(str); // recommended because size_t will automatically maps to 4 bytes in x86 and 8 bytes in x64

13\. C11 introduced the concept of static asserts. These are basically conditional asserts which can be evaluated during compile time. So C11 has a new keyword called _Static_assert(expr, message) The reason for this ugly name is the same idea of not to break existing code. so for convenience assert.h header provides static_assert macro which mean the same. One of the use of static asserts is below

        struct book {
          int pages;
          char author[10];
          float price;
        };

        static_assert(sizeof(struct book) == sizeof(int) + 10 * sizeof(char) + sizeof(float),
                      "structure contains padding holes!");

14\. Another good video on some low level details - Storage Duration and Linkage in C and C++ - Dan Saks

15\. #define _CRT_SECURE_NO_WARNINGS can be used to disable CRT warning for common functions.

16\. Any ucrt function which begins with _ is a non standard api provided by ucrt. For example in string.h's _strdup, _strlwr, _strrev are some. The take away here is, it is easy to identify which function is part of C standard and which are not. Interestingly some(not all) of these non standard functions are part of posix so in glibc(which implements posix) don't have _ in them.

17\. All posix function in posix standard with [CX] annotation indicate Extension to the ISO C standard for example, below function from stdlib.h is posix extension. UCRT defines a similar api called _putenv, since this is not part of C standard, UCRT version has an _

        stdlib.h - <USER4>
        [CX] int setenv(const char *, const char *, int);
        stdlib.h - <USER5>
        int _putenv( const char *envstring );

        stdio.h - <USER4>
        [CX] int fileno(FILE *);
        stdio.h - <USER5>
        int _fileno( FILE *stream );

18\. Learned about CGold: The Hitchhiker's Guide to the CMake. An awesome tutorial about CMake. Now it is super easy to start a C project without worrying about the individual build systems.

        # CMakeLists.txt - minimum content
        cmake_minimum_required(VERSION 3.4)
        project(command_line_parser)
        add_executable(command_line_parser main.c)

        # commands to run to generate the respective native build files like vcxproj files
        # In below command -S standards for source directory path.
        # -B stands for final directory where vcxproj files are generated
        # CMake only generate one flavor (x64/x86) per project file, here we are generating x64 by specifying x64
        cmake -S . -B builds -G "Visual Studio 16 2019" -A x64
        # we can also use cmake-gui to do the above

        # Once vcxproj files are generated we can either directly build the proj files using Visual Studio
        # or better use cmake itself to build it for us from CMD using msbuild
        cmake --build builds

Hope these help.

<comment><USER29> > _t suffix is the notion used to denote a typedef for a given type /* This > example is from do_journal.c in e2fsprogs / struct journal_transaction_s { > ... blk64_t start, end; ... }; typedef struct journal_transaction_s > journal_transaction_t;

This is undefined behavior in POSIX.

<comment><USER13> And

        unsigned total(unsigned a, unsigned b) { return a + b; }

is undefined behavior in C.

</comment></comment></comment></comment></comment></comment></comment></comment></comment></comment></comment></comment></comment></comment></comment></comment></comment></comment></comment></comment></comment></comment></comment></comment></comment></comment></comment></comment></comment></comment></comment></comment><comment><USER91> Radical suggestion: read the C standard.

<comment><USER92> Asking someone to read a multi-hundred page standards document just to learn the language is wholly unreasonable.

<comment><USER13> Well, OP asked for source of truth. Nothing comes as close as the standard itself.

Honestly, I think any programmer writing C today should have it around for reference. And yeah you kinda need to read it too, or you won't be able to refer much.

That said, no need to read it cover to cover. There's stuff one can earmark as being there but ignore until it's actually needed (for example: the grammar and all the library functions).

And speaking of _tedu_ , I would recommend to the OP that they get in the habit of checking out the OpenBSD man pages for libc functions.

<comment><USER6> I actually have very few $200 PDFs lying around.

Do you really go purchase the standard documents from ISO? Or does your employer? I'd love to have the actual standard "around for reference," but 700-page technical documents from ISO are not cheap.

<comment><USER13> The next best thing is the latest draft standards, which are available online for free (and I always have them on my laptop, PC, and personal server). Differences between them and final official standard are not important. Search for N1256, N1570. Or grab them here. [http://www.open- std.org/jtc1/sc22/wg14/www/standards](http://www.open- std.org/jtc1/sc22/wg14/www/standards)

(There are more readable html versions floating about)

</comment></comment></comment></comment></comment><comment><USER93> Screw C, really. Let this crappy old language die off quietly. Learn Zig (the best replacement for C) or at least Rust.

</comment><comment> ryan-allen I'd check out Zed Shaw's book [0]. It's opinionated, but I think that's a good thing for something like C.

It's broken up into exercises that you start working through straight away, and you start early with valgrind.

[0] [https://learncodethehardway.org/c/](https://learncodethehardway.org/c/)

<comment><USER29> The author of that book doesn't really like C, and it shows. And valgrind is usually not the first tool you should be using to debug memory issues–try Address Sanitizer instead.

</comment></comment><comment><USER94> C is great for learning about how computers work at a low level. In my college, we started by writing a simple compiler. This should force you to understand pointers and memory which, as others have mentioned, are fundamental. You'll have to both know how the assembly works and to write correct C code. So writing a simple compiler will force you to understand it two different ways at once. My opinion is you'll learn more by diving in and doing things, especially with your existing programming background.

But, for your own sanity and everyone else's, do not start new projects in C! (Aside from purely academic ones for you to learn.) The point of a programming language is to help humans write safe and correct code. In this sense, C has completely failed. The syntax itself is just begging you to make a subtle mistake. And due to lack of memory safety, even the most well tested and scrutinized projects in C/C++ (such as Chromium) suffer from buffer overflows or memory leaks, potential security vulnerabilities that are automatically prevented by design in other languages. If you need to do something low level with any sort of confidence, use a memory-safe language like Rust, which can even do interop with existing C libraries and code.

(Edit: typo)

<comment><USER95> It depends on what you're doing. Low level stuff pretty much has to be C. But for applications I agree there are much better alternatives these days.

<comment><USER94> Yeah, there may be some situations where C is necessary as a wrapper for assembly if you're _extremely_ memory constrained or something like that. But I wouldn't have any confidence in it working as intended unless it's dead simple.</comment></comment></comment>


The Sci-Fi Sex Scene That Changed My Life - <AUTHOR> https://slate.com/human-interest/2018/07/asimovs-the-robots-of-dawn-and-the-sex-scene-that-changed-my-life.html <comment><USER2> Semi-related, the first time I feel like I was ever exposed to the concept of transgender was in the science fiction book Commitment Hour by James Alan Gardner

"The novel is set in Gardner's "League of Peoples's" futuristic universe, and plays out in the small, isolated village of Tober Cove. Set on post- apocalyptic Earth, Tober Cove most resembles a rural, seventeenth century fishing village, with one exception: every year, everyone below the age of 21 changes gender. At the age of twenty-one, the people of the village must "commit" to being male, female or both in the form of a Hermaphrodite (a 'Neut'), forever"

While I've never identified as anything other than my biological gender, when I read this around age 12-13 I thought "wouldn't the world be incredibly different if choosing your gender was a thing". Little did I know that 20 years later I'd be working with a female to male and a male to female and the Garbage song Androgyny would seem far less obscure and an actual reality.

Science Fiction does a lot for many issues and fields, I wish more people would read (and reflect on) well thought out science fiction.</comment>


12 (Really) Controversial Programming Opinions - <AUTHOR> http://www.billthelizard.com/2012/09/12-really-controversial-programming.html <comment><USER2> Mostly opinions of an angry C programmer. Disagree with all of them. Too context specific. For example, testing. It is generally a good idea, but you don't always want to invest the time if it will have a negative return. Small projects or throw away work are two conditions where this would be true.

<comment><USER3> These are NOT his opinions but the opinion of several _different_ people who answered the original StackOverflow question.

The first one, for example, is obviously a joke.

</comment></comment><comment><USER4> guys,

>The first language should NOT be the easy one, it should be >one that sets up the student's mind and prepare it for >serious computer science. >C is perfect for that, it forces students to think about >memory and all the low level stuff,

as one low-level programmer to another, I hate to break it to you, but "low level programming" is not "serious computer science". the two have very little to do with each other.

<comment><USER5> That's pretty true. However, in C, you can actually write interesting data structures and algorithms that come up in computer science. I think that makes for good "theory meets practice"-based learning.

I'm not saying all algorithms classes should incorporate writing C code all the time. But probably every budding computer scientist or software engineer should take at least _one_ such class.

I'm also not saying C is the only language for which this is true. But it's definitely not generally true for interpreted languages.

Also, C is pretty much the language everything higher-level is implemented in. So once again, at least some exposure to C can help someone conceptualize what's happening on the computer at every level.

<comment><USER6> Hmm, can you give an example of a typical "learning CS" level algorithm that you can't implement in, say, JavaScript?

<comment><USER7> Not an algorithm, but used in many. Pointers.

<comment><USER6> Emulating pointers in JS is pretty easy; you just wrap the values in objects or arrays.

</comment></comment></comment></comment></comment><comment><USER8> One that really stood out to me:

 _6\. The use of try/catch exception handling is worse than the use of simple return codes and associated common messaging structures to ferry useful error messages._

I think this really depends on the language. In erlang I absolutely hate exceptions because they don't really make sense in a functional language (which let's say erlang is for the sake of this argument). Threads run functions with only the function's parameters as state. It doesn't make sense to have to worry about a function _stopping_ half way through, especially when most functions already pass up tuples looking like {error,E} or {success,S}. If you have to worry about exceptions as well, now you have to handle the error tuple and the exception case, and usually both cases are getting the same code so you have to go make a new function to call for that case. This gets messy. If a thread crashes it crashes, otherwise it should be passing up the error tuple; there's no reason for exceptions.

On the other hand I think exceptions make a lot of sense in python, and I think the python community has incorporated them in a standard way which makes sense, so that handling exceptions in python is actually easier then passing back different error codes and what-not (no atoms in python).

</comment><comment><USER9> I do sort of agree with the try/catch criticism. I know what it achieves and why we need it but I don't agree that it's the best or even most intuitive error handling mechanism. I personally prefer returning error messages in place of or alongside expected data structure outputs, such as the common paradigm in JavaScript where async functions return error info (or null) as the first parameter in the supplied callback before returning the actual data (if no error) in the subsequent parameters.

I do somewhat agree with the OOP criticism as well. I find the best programming achievable is by mixing strategies and not adhering strictly to any one philosophy. Java's OOP is so inflexible and limiting that it takes a significant amount more effort to get basic work done. It may lead to a somewhat more manageable codebase at first, but I feel experience in your language of choice can bridge the gap between such a strict static-typed language vs a more elegant but potentially messier dynamic-typed language in this respect.

Finally I do agree that C should be taught before you learn any other programming language...but not to such a degree that you need to be able to build the best Javascript engine in the world from scratch before you move onto another programming language. The goal of programming is to build software that saves you time, after all - you aren't saving much time with as high a barrier to entry as mastering the fundamentals of C. It is important for theory discussion as well as learning the best way to code a lot of logic in many other languages, but I feel an abstracted language has failed if it hasn't done enough of the work for you to refactor your code properly in the first place. Loop unrolling, seriously? I like that I learned why it's important...but I shouldn't ever have to do that in any decently programmed higher level language.

<comment><USER10> I don't. Exceptions in general FORCE a developer to deal with the problem. They can slap a developer in the face if they're abusing the API.

Error codes on the other hand can be ignored.

PutLionInCage();

PokeLionWithStick();

I'd rather PutLionInCage() throws if there is a problem.

<comment><USER11> _Exceptions in general FORCE a developer to deal with the problem._

There are good arguments in favour of exceptions, but I don't think this is one of them. If Java has taught us anything, it is that trying to force someone to handle exceptions just causes a lot of code that says:

        catch (Exception e)
        {
            // silently ignore e
        }

<comment><USER12> Actually, the correct comment for an empty catch block is:

// This should never happen.

(Until it does)

I have mixed feelings about checked exceptions. They seem like a great idea in theory - if there are known error states, then making them explicitly known and forcing the consumer to acknowledge them in some way seems reasonable. In practice, a lot of the time the only response to a checked exception is "let it propagate up - there isn't anything useful I can do", which leads to extra code and can be annoying.

Even though I don't have quite the negative feelings toward them others do, if I were involved in designing a new language today, I'd probably vote against including them. It would possibly be nice if a method could explicitly declare which exceptions it was likely to throw, and then an IDE or static analysis tool could use those to aid consumers of the API, without forcing them to deal with every single one.

Interestingly enough, I view Scala's _Option_ construct as being somewhat similar - forcing the consumer to acknowledge that a particular call could return a null value - but it doesn't seem to provoke the same negative visceral reactions that checked exceptions do.

</comment></comment></comment></comment><comment><USER13> There can only be controversy when either the truth isn't clear, or it doesn't matter. If we knew the answer, and it mattered, everyone would do it that way (and those who didn't would lose to those who did). e.g. religious wars:

      vi vs emacs
      OO vs fp

<comment><USER5> I don't agree at all. I don't think Galileo would agree, either.

</comment></comment><comment><USER14> One opinion of mine is "code is also configuration". If you structure the code right, I see nothing wrong with putting stuff that many people see as configuration in the code. I do Java programming, and I think that frameworks like Spring are overrated. There is nothing very different from stitching together dependency injection with XML than with Java code. It is a pattern, not a framework. But the Spring trend is hard to walk against, it is so strong. I am getting tired of this occupation.

<comment><USER15> I err on the side of <http://en.wikipedia.org/wiki/Rule_of_least_power>. While it's true that code is data, arbitrary code is a form of data that's exceptionally hard to analyze or repurpose. Text config rules out stuff like dynamically-composed names of objects and properties (which you can't search- and-replace).

</comment></comment><comment><USER16> I disagree with most of them and seemed to be biased towards a specific language the poster of the opinion liked/understood best or against one they disliked.

</comment><comment><USER17> Really surprised at the amount of hate for Python significant indentation. I've only used Python a little, and I expected to hate that too, but found it wasn't really so bad. I thought my experience was pretty common, and that by now most people either thought it was a great idea or at least found it tolerable. But "it was a bad idea" is currently favored 43 to 5. Maybe not many Pythonistas have seen this page yet.

<comment><USER18> In my opinion not many Python programmers care for places like stackoverflow, as the language itself has excelent infrastructure to deal with problems and to get ones questions answered quickly without bothering other people. Maybe that's why the vote is so high there.

Everyone working with Python a little while won't even think about indentation anymore, it just comes natural. It's not a big deal.

Python is often made out in discussions like that to be for begginners only and only amateurs would use it because its slow, not concurrent etc... The truth though is that YouTube, Reddit, Dropbox, Spotify, Discuss, Google, Nasa and many other "real" professionals use Python in a big way.

I tend to ignore such trolls.

</comment></comment><comment><USER19> 13\. Most programmers think they know what they're doing but actually are "incompetent". (There are Usenet posts going back to 80's documenting this fact.) How am I defining "incompetent"? In my definition it means writing software that any reasonably skilled hacker can cause to "malfunction", i.e. not perform as expected by the programmer, which depending on the program may or may not present a security risk. The number of "competent" programmers i.e. those whose programs are immune to the acts of reasonably skilled hackers, is very small. They are a rare commodity. In sum, there's lots of reasonably skilled hackers and very few competent pogrammers. There is no licensing body for programmers. Anyone can call themselves a "professional programmer".

Note: If a programmer only writes programs for her own use, "competency", as defined above, is all but irrelevent. Whatever works.

Controversial, but true.

<comment><USER15> Knuth's code has bugs. NASA's code has bugs. I don't think our species has produced even one nontrivial correct program, much less a programmer who's competent (which I take to mean a large portion of their work is 100% correct). Our profession is passing through the mercury-and-leeches phase medicine once went through. Someday our descendants will look back from their error monad formal proofs or something and cringe at everything we did.

<comment><USER20> >I don't think our species has produced even one nontrivial correct program

Off the top of my head: seL4, a correct microkernel...pretty nontrivial!

<comment><USER15> Wow, I'm pleased to see that's no longer totally beyond us, though the price is still very high (LtU says fifty dev-years!)

<comment><USER20> It was actually about 28 py, but that number would come down to 10 if they were to do it again.

source: <http://ertos.nicta.com.au/research/l4.verified/numbers.pml>

<comment><USER19> This is nothing compared to the number of py spent writing crapware. Think of how much effort has been spent writing lousy software. It is enormous. (But then most consumers of software don't know any better, so from a sales perspective, maybe writing crapware makes perfect sense.)

IMHO, these guys are heros merely for undertaking the task, let alone completing it.

</comment></comment></comment></comment></comment></comment><comment><USER21> I don't agree with many of the points, but having taught a lot of beginners while loops, they are often very confusing to people.

I usually end up explaining them like a repeat...until, which usually makes more sense to non-programmers.

</comment><comment><USER22> Wow, mostly terrible advice.

<comment><USER23> Can you elaborate?

<comment><USER22> I find it odd that I have to, other than 7 and 10, which I'm not saying are necessarily always good advice, the rest are just terrible advice that I think are self evidently terrible advice. Experience has taught me nearly exactly the opposite; the advice strikes me as bad programmers who only know one language generalizing their poor procedural skills across domains they don't understand.

Of course, that's the point of the article, "Really" controversial. Seems most people voted those opinions down as poor advice, which they're correct in doing.

</comment></comment></comment><comment><USER24> A linkbait, "N Ys that X" content-farmy headline that seems just tailor-made for getting on HN.

<comment><USER25> Actually I think that it's a more ironic take on the meaningless links that made the front page (twice I believe) a while ago about some (not so) controversial opinions on software development. I certainly hope no one thought any of these were opinions to be taken seriously.

</comment></comment><comment><USER26> Was this article just one big troll?</comment>


First-person Tetris - <AUTHOR> http://www.firstpersontetris.com/ <comment><USER2> Darn, I was hoping for a perspective from the block itself looking down on the already placed blocks.

Second-person tetris then?

[on a side note, I really need to learn how to game the front page. 2 year old, minute twist on 30 year old game -> front page. write a new game from scratch -> dustbin]

<comment><USER3> like Blockout from the 90s? [https://www.google.com/search?um=1&hl=en&biw=1920...](https://www.google.com/search?um=1&hl=en&biw=1920&bih=953&tbm=isch&sa=1&q=blockout&oq=blockout&gs_l=img.3...0.0.0.3622.214.171.124.0.0.0.0.0..0.0...0.0...1c..6.img.rR7hL6R1Vao)

<comment><USER4> I immediately thought of Welltris: [https://www.google.com/search?um=1&hl=en&biw=1920...](https://www.google.com/search?um=1&hl=en&biw=1920&bih=953&tbm=isch&sa=1&q=blockout&oq=blockout&gs_l=img.3...0.0.0.3622.214.171.124.0.0.0.0.0..0.0...0.0...1c..6.img.rR7hL6R1Vao#um=1&hl=en&safe=off&tbm=isch&sa=1&q=welltris&oq=welltris&gs_l=img.3..0j0i24l6.24534.26173.0.265126.96.36.199.0.0.0.179.734.6j2.8.0...0.0...1c.1.6.img.MdSBBwzo3Kk&bav=on.2,or.r_cp.r_qf.&bvm=bv.43828540,d.bGE&fp=ec8fca4e9cb74de&biw=1920&bih=1085)

</comment></comment></comment><comment><USER5> Another fun (for some value of fun) variant is Hell Tetris[0] based on xkcd[1].

0 - <http://www.kongregate.com/games/banthar/hell-tetris>

1 - <http://xkcd.com/724/>

<comment><USER6> Perhaps more interesting is Hatetris <http://qntm.org/files/hatetris/hatetris.html> which is programmed to always give you the worst possible piece. Just try to clear a line!

<comment><USER7> Well it always gives the same piece... Of course you can't finish a line. What does it prove?

<comment><USER8> It doesn't give you the same piece every time. If it did, you could finish a line just by lining each S-shaped piece on it's long end.

I managed to pull off a 4-line score: 20C2 AAAA AAAA 8CAA AAAA AAAA AEAA AAAA A5EA AAAA AD5A A2AA 0000 AA32 0AAA AAAA B0A9 552A AAAA ADAA AAAA B62A AAAA AB2A AAAA A8C2 AAAA AAA3 2AAA AAAA D58A AAAD AAAA A8C0 2AAA A8C2 2AAA AA39 AAAA B0AA AAAA 3AAA A8C8 AAAA A36A A3AA A32A AA30 02AA 8C0A AA8C 2AA9 5DAA AAAA AA9D 55AA AAAA AA75 5AAA AAA9 55AA AAAA 15AA AAA9 D54A AAAB 9AAA 46AA A808 AAA0 AAA1 556A AAB5 55AA AAAA

<comment><USER9> I used the same strategy to get 6:

C00A AAAA AAAB 0AAA AAAA ABAA AAAA AAB5 AAAA AAAA B000 AAAA AAAD 52AA AAAA AC02 AAAA AAAC 0AAA AAAA C2AA AAAA ACAA AAAA AEAA AAAA ADAA AAAA AD6A AAAA C00A AAAA C02A AAAA C0AA AAAC 2AAA AACA AAAA EAAA AAD4 AAAA AC00 AAAC 02AA AC0A AAC2 AAAC AAAD 2AAA D55A AAAA AAA9 5DAA AAAA AB5A AAAB 5AAA B00A AB0A ABAA 9D4A A975 AAAA AAB5 56AA AAAC 02AA C2AA EAAD 52AA D56A AAAD 55AA A956 AAB5 AAB1 AAB0 AA8C 1756 6A01 03AA 2A

</comment></comment></comment></comment></comment><comment><USER10> Take a look at not Tetris 2[1]. From its description:

Not Tetris 2 is the spiritual successor of the classic Tetris mixed with physics. The result is a fun spinoff in which blocks are no longer bound to the usual grid. Blocks can be rotated and placed at any angle, resulting in a complete mess if not careful. And with the newest cutting edge technology, Not tetris 2 allows line clears when the lines are sufficiently filled.

[1] <http://stabyourself.net/nottetris2/>

<comment><USER11> I've played this one too much. Very fun.

</comment></comment><comment><USER12> Interesting idea! I wouldn't recommend trying to play this after being on the vino all night though.

Excuse me while I find a bucket.

</comment><comment><USER13> The fact that the orientation of the view changes based on the orientation of the block necessarily precludes this from truly being "first-person Tetris." (Unless the implied viewer can rotate about an axis that is perpendicular to the screen, which seems absurd.) Anyway, I digress.

It's still a neat concept...

</comment><comment><USER14> The first time I saw this was in 2010. Ah, those were the days.

<https://news.ycombinator.com/item?id=1052389>

</comment><comment><USER15> Very cool indeed, especially because the concept is not something astonishingly complex.

Check out _NightMode_ and _Exsistential Crisis_

I consider the latter one to be the crown of this concept.

It looks even better and you have to memorize the structures laid, which makes it more interesting (especially since once you've grasped the original idea, the normal mode is not that hard at all)

<comment><USER16> +1 Existential crisis is really cool because it adds a memory game to the mix

</comment></comment><comment><USER17> Warning, motion-sickness trigger.

<comment><USER18> Ugh, yes. I didn't think I could feel queasy from looking at a computer screen (outside of, say, staring at bad PHP code), but this thing set off my stomach.

</comment></comment><comment><USER19> Did playing that make anyone else nauseous?

<comment><USER20> yes. I discovered this a few years ago, and used to play it as a break from work - it is fantastic at _making you stop_ after a while because you're so dizzy! :)

</comment></comment><comment><USER21> I was hoping for a first-person 3d tetris

but the way it's made here, I think what it's missing is that gravity should be replaced by maneuvering to whatever side of the square is "up"

like aiming a spaceship

that would be challenging to play!

</comment><comment><USER22> Also old, but since we are on the mood: <http://www.tetris1d.org/>

</comment><comment><USER23> This is by David Kraftsow aka dontsave (<http://dontsave.com>). His projects are consistently awesome. Check out <http://yooouuutuuube.com>

</comment><comment><USER24> I have a feeling this is going to make the tetris effect much more extreme.

</comment><comment><USER25> I thought it was fun to just hear the original music again...

</comment><comment><USER26> This is brilliant! But I find the key mapping much harder to adjust to than the perspective. I'm far more used to 'k' to rotate and space to drop.

</comment><comment><USER27> I was, sadly, hoping for Resident Evil 1/Tomb Raider 1 style controls where up is ALWAYS forward. It increases difficulty ten-fold.

</comment><comment><USER28> Reminds us that Flash isn't dead.

<comment><USER29> Ah, that's why all I saw was a black page. They don't even bother to do notification of errors these days.

<comment><USER30> Same for me. Such games should have a landing page with a screenshot, or at least some explanation.

</comment></comment></comment><comment><USER31> This is so entertaining. Thanks for sharing. I will spread the word on this one!

</comment><comment><USER32> This is gives me a serious headache! Try playing this whilst drunk....

</comment><comment><USER33> This is not what I thought, but it was just as awesome. Great job!

</comment><comment><USER34> Actually one of the highest scoring games of Tetris I've played.

</comment><comment><USER35> I was so looking forward to a Doom/Tetris mashup. Disappointed.

</comment><comment><USER36> This is so much more challenging than regular tetris!

</comment><comment><USER37> Awesome, but that just gave me a headache! lol

</comment><comment><USER38> IIII ... t's .... something !!! :)

</comment><comment><USER39> TYPE 1 of music is pleasant

</comment><comment><USER40> It gave me the dizzies.

</comment><comment><USER41> the controls are goofy.

Why does the down button suddenly become the up button when the block rotates. Shouldn't it stay the same?

It _does_ add an extra level of challenge though! :)

</comment><comment><USER42> Ghastly. Who upmodded this? Anyone want to admit it?

</comment><comment><USER43> 8 bit music :)

</comment><comment><USER44> woa not an easy game. Sweet idea!

</comment><comment><USER45> awesome!

</comment><comment><USER46> old... but cool :)

</comment><comment><USER47> i hate this game already</comment>