
As a system programming language, C still deserves learning today - nanxiao
https://nanxiao.me/en/as-a-system-programming-language-c-still-deserves-learning-today/
======
lifthrasiir
My view strongly aligns with John Regehr's [1] (emphases original):

> My main idea is that we need to teach C in a way that helps students
> understand why a very large fraction of critical software infrastructure,
> including almost all operating systems and embedded systems, is written in
> C, while also acknowledging the disastrously central role that it has played
> in our ongoing computer security nightmare. [...]

> We’d like students to be able to answer the question: _Is C an appropriate
> choice for solving this problem?_ [...] The second big thing each student
> should learn is: _How can I avoid being burned by C’s numerous and severe
> shortcomings?_

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

~~~
jonv
"How can I avoid being burned by C’s numerous and severe shortcomings?"

I think this is where we start bumping up against the limitations of the
universe we live in, because the question becomes "where do we put the bugs?".

The biggest flaw with C is the array bounds checking, especially if that array
is on the stack, or other bugs related to "raw" pointers:

\- We could enforce things through hardware, but then you've put the bugs in
the microcode.

\- You can change your language, but then you've put the bugs in your compiler
/ interpreter / vm.

\- You can try and formally verify the correctness of your code, but it's less
feasible for large codebases. And what if there's a bug in your proof? (proofs
are programs)

I'm not suggesting people use C/C++, but I wouldn't blame C for the problem of
natural numbers.

~~~
oblio
> \- You can change your language, but then you've put the bugs in your
> compiler / interpreter / vm.

I'd rather put my bugs in one, more easily verifiable place. Verifying all
code that's ever going to be compiled is close to impossible. Verifying the
compiler / interpreter / VM is hard, but it's much easier.

At the end of the day our field is basically: "get rid of repetitive work". So
arguing that it's better to have total manual control (and basically always
re-implement things at the point of use) doesn't strike me as the best
approach.

~~~
jonv
"I'd rather put my bugs in one, more easily verifiable place." We are in
agreement, that is why I was saying people shouldn't use C unless they need
it.

------
fit2rule
In my opinion, as a programmer who learned C when it was still in its infancy
as a language, and who has used it in nearly every single project in 30+ years
of professional development, what we need to do is not just teach the C
LANGUAGE, but also the C RUNTIME, EXECUTION ENVIRONMENT, and .. most important
of all .. the C COMPILER.

Each of these aspects of the "C ecosystem" needs to be well understood in
order to be a productive, high-quality C developer. Its not enough to just
type a bunch of stuff and then throw it at the compiler and see if it works -
you have to understand what the compiler is doing with your language
constructs and how this will be executed in the target execution environment.

So many times I've had to debug "professional C developers" who have no clue
what the compiler is doing to their code, no idea what a TEXT SEGMENT is,
absolutely zero responsibility for the HEAP, let alone runtime loading and
linking. Its all just 'voodoo' behind the 'black box' of the compiler.

But even just having a basic understanding of these components can mean a huge
difference in quality C code.

Another thing every C developer needs to know: how to debug code and read
assembly language in the context of the operating/execution environment. You
don't need to be able to WRITE assembly, but at least fire up the debugger and
step through your program a few times to see how it behaves .. this can go a
long way towards increasing a C developers understanding for what is happening
and why its important to know all the other components. Too many times I've
solved a major, delaying bug in a project by just re-ordering the linker
commands or, even worse, cleaning up the linker commands to remove stuff that
was glibly thrown at the wall by some other dev.

Also - all warnings are errors: no exceptions.

~~~
forgottenpass
Any reading recommendations on how libc, libstdc++ and the compiler all
interact, and how to manage that when the target OS is older than the
toolchain?

~~~
fit2rule
The source. Yes, really.

~~~
forgottenpass
Yeah, I know. Still worth asking though.

Part of me misses working on easily googleable things.

------
gilmi
> There are many ways of writing wrong C code, but you only need to make sure
> what you write is correct and in defined behavior, that’s all about C
> programming.

But this is a something that even experts fail to do.

~~~
hburd
Is undefined behavior really that bad when it's not causing problems?

~~~
kazinator
"Undefined behavior" is a broad area which covers everything from defects in a
program that make it crash, to _documented_ language and library extensions
(which real-world programs can hardly avoid using).

------
qiqitori
If a programmer can't at least read (normal) C, I'd doubt they're a fully
qualified programmer. (Just my way of viewing things, I might be wrong. My
reasoning is that if you don't know C and pointers, you probably don't know
what a simple line like 'foo = bar + "abc"' even entails.)

It's a tiny language, has easy syntax, and "undefined" behavior (which you
don't normally run into) exists for a reason -- e.g. to avoid having to check
for unlikely cases every time a heavily used function (e.g. memcpy) is called.

~~~
kasperni
If a programmer can't at least read (normal) assembler code, I'd doubt they're
a fully qualified programmer. (Just my way of viewing things, I might be
wrong. My reasoning is that if you don't know assembler code, you probably
don't know what a simple line like 'foo = bar + "abc"' even entails.)

My point being, that it is a bit arrogant claiming that somebody isn't a fully
qualified programmer unless they understand X technology. In my book you can
be a fully qualified programmer (what ever that entails) if the only
programming you have ever done is in Excel.

~~~
habnds
Comparing C to python, the lack of explicit memory management and pointers in
python make it hard to understand what the machine is actually doing compared
to writing in C.

Are there equivalent examples between C and assembler? What sort of conceptual
stuff is C missing that assembler clarifies?

~~~
foota
You could argue it's not conceptual, but you don't have to deal with registers
in C. Also system calls. C handles dynamic library loading, I think. Not too
familiar with how that works in assembly.

~~~
kazinator
You have to deal with those register when debugging C, at least from time to
time. C debuggers show you the registers; e.g. "info reg" in the GNU debugger.
Linux kernel debugging often means working from a dump of the CPU registers,
raw memory around the stack pointer, and surrounding the instruction pointer.
From that you figure out what bad thing the C code did.

------
_hardwaregeek
Hmm, I'm very skeptical of this premise. Basically every halfway decent CS
program has a systems level class in C. While bootcamps usually don't teach C,
they're not necessarily dismissing C as much as ignoring the entirety of
systems programming.

I don't want to be overly harsh on the writing, as English does not appear to
be the author's first language (you should see my Chinese). However, here's
some pointers (heh)

Especially with Go and Rust go viral right now => Especially with Go and Rust
having gone viral

No matter you never touched C or you are a veteran => Whether you are a C
novice or a C veteran ^[1]

and not as primitive => and is not as primitive

Regardless of you a system language programmer => Regardless if you are a
systems language programmer

In general, I'd recommend using "you" a little less frequently and working in
more. I.e. "I highly recommend you read Modern C if you haven’t read it
before" could become "I'd highly recommend Modern C to those who have not read
it already".

But again, wonderful job all things considered. Please continue to write blog
posts.

[1] I tried to rewrite this in the spirit of the original, but the double
negative of "No matter...never" was too awkward to keep. Also feel free to
remove the C's

~~~
yaa_minu
> However, here's some pointers (heh)

However here are some pointers (heh) :D

[https://en.wikipedia.org/wiki/Muphry%27s_law](https://en.wikipedia.org/wiki/Muphry%27s_law)

------
keithnz
I think if you are interested in systems or embedded programming C is
definitely a good language to learn still. I programmed in C ( and C++ ) most
of the 90s, focused on C++ and other languages for the 00s, then came back to
it 2010ish, and I found it refreshing using modern techniques in C. However,
I'd still say avoid it if you don't have to use it. But the key here is
knowing exactly what you are trading off before choosing to avoid it. In most
cases C++ is a viable alternative ( not always ). Rust is starting to look
good in the embedded world, but I'd like to see more vendor support for it,
and very few professional Rust devs in the embedded space. C is still often
the better tradeoff. Also Electronics Engineers often only know C which lets
them debug and diagnose hardware. So in the embedded world, you should know C,
in the systems world, its a good idea, it will let you read a large majority
of all open source system level software

------
PinkMilkshake
I would rather resist the temptation if I can avoid it. I'm so frustrated with
what we've come to tolerate with software.

Ada/SPARK seems to be trying to make a comeback, something I welcome. They are
interacting with the 'maker community' [1] and the online learning resources
have improved a ton.[2] SPARK is even taking some inspiration from Rust. [3]

[1] [https://www.makewithada.org/](https://www.makewithada.org/) [2]
[https://learn.adacore.com/](https://learn.adacore.com/) [3]
[https://blog.adacore.com/using-pointers-in-
spark](https://blog.adacore.com/using-pointers-in-spark)

------
adzm
What I find fascinating about C is how mature codebases develop their own meta
language with copious macros. These in turn inspired future languages and
their built-in capabilities.

~~~
fit2rule
My current preferred language/runtime/execution environment is: just enough C
to support the Lua VM.

Write low-level stuff in C as necessary, expose it as an abstraction to the
Lua VM, write all app logic in Lua.

This really represents a great bridge between two worlds and I've just found
it so incredibly productive.

Disclaimer: grey-beard C dev who doesn't need to learn your new-fangled
language that 'fixes' everything (npm, lol) because I already did it decades
ago with Lua ..

;)

~~~
Reelin
What's your impression of Lua-Terra?
([http://terralang.org](http://terralang.org))

~~~
fit2rule
Lua-Terra is badass. :)

------
noobermin
One thing the internet as still not learned is how not to needlessly flame. A
lot of the comments here are dissing the problems of C (fine) but are
completely missing the author's main point. For heaven's sake, it's even a
lower bar this time, the title says it all and people are cursing about C.
Their main point is simply:

>Especially with Go and Rust go viral right now, C seems already forgotten by
people. Nevertheless, IMHO, C is still a thing which is worth for you spending
some time on it.

I happen to agree, especially if you have time to learn Go or Rust but don't
use it for anything[0], you have time to learn modern C. They didn't make this
point (they seem more on the side of you should in general) but if you don't
have the time, I'd think anyone could make a decision of what's worth their
time or not.

As an aside, this bit is partly true but partly isn't:

>Last but not least, because C is so “low-level”, you can leverage it to write
highly performant code to squeeze out CPU when performance is critical in some
scenarios.

The reality is C has been around a long time and compilers are written to make
fast code from C programs. See this, "C Is Not a Low-level Language; Your
computer is not a fast PDP-11."[1]

[0] Point about Go or Rust is if you do intend to use it for something, then
that isn't learning for edifications' sake. Ditto C. If you refuse to learn C
but it's relevant to your job/work then that's simply neglect of your duties.

[1]
[https://queue.acm.org/detail.cfm?id=3212479](https://queue.acm.org/detail.cfm?id=3212479)

------
todd8
I've programmed for many years and taught programming; my trajectory through
the various programming languages and paradigms seems to have worked for me.
But things have changed in big ways since I learned to program.

My first job after grad school was as an assembly language programmer. It's
humbling and teaches some good programming practices (like "take a small bite
and then run tests"). Today, assembly language programming is not appropriate
for most applications. Processors are very fast, memories are very large, and
instruction sets include numerous complex features that optimizing compilers
handle very well. Meanwhile there has been one new programming language after
another gradually improving the high level programming tools available.

If I was designing a university curriculum for CS, I would introduce languages
in this order:

First Python, its basics are easy to learn and it allows new programmers to
actually tackle interesting assignments. Those that don't go on in CS will
have still acquired familiarity with a useful language for writing simple
programs. It's a good language to use as a lingua franca in subsequent
courses.

Second simple Java and OO design, there are just so many jobs using this
language.

Third C, introduced along with a study of data structures. Learning data
structures as an undergraduate is mostly learning linked structures of one
kind or another and C is the best language for this. C is close to what's
happening at the machine level whereas Python and Java have garbage collection
and language features that already include lists, maps, etc. Studying data
structures in C lets students see how these higher level abstractions are
implemented and prepares them for seeing kernel code.

After this there are still some big important languages left out. Lisp/Scheme
can be introduced in an AI class. Javascript can be introduced in a web
programming class. Assembly language can be introduced in a hardware class.

The go language would work well for an algorithms class and "modern" Java with
collections, streams, modules, etc. for an undergrad compiler class or
software engineering class.

Naturally SQL should be used in a database class. Git could be introduced
after the first year and used for all assignments. LaTeX could be required for
all writing assignments.

The important language left out here is C++. It's just seems so difficult to
learn that I don't know when or whether it should be taught. C++'s new
features are big improvements, but the historical baggage is a lot to take on
as a new programmer. When should C++ (or Rust, or Haskell for that matter) be
learned?

~~~
blub
C++ lends itself well to large-scale, performance-intensive, cross-platform
software. Rust could in theory be used for similar projects.

Using C to learn pointers & data structures gives students just enough rope to
hang themselves later if they want to use C in a real project. I'd go for an
STL-heavy C++ and dig into the memory handling and pitfalls.

Students need to understand how to _solve_ resource management issues in low-
level programming. Since they're essentially unsolvable in C (be careful, try
harder, use valgrind & pray), it doesn't seem like a good teaching language.

------
cheerioty
In software development (web/mobile) for about 10 years, I finally picked up
learning C. 4 weeks in, I finished writing my first Gameboy (Color) game and
started work on my first Neo Geo game with awesome progress already. Will
share more soon, but the gist is that you can't do this in JavaScript, Go or
Rust. And don't say GBStudio now :)

------
pjmlp
There are two main reasons to learn C:

1 - Because C and UNIX were built for each other and we have plenty of UNIX
clones around

2 - To avoid making the same mistakes regarding lack of safety in system
languages.

------
ndesaulniers
> I highly recommend you read Modern C if you haven’t read it before.

I feel like I was pretty disappointed with Modern C, contrasted with Deep C
Secrets which taught me a ton. Maybe I should write a book about all of the
GNU C extensions and the situations in which they are useful (or the only
possible solutions).

~~~
AlotOfReading
Honestly, working with gcc is the closest thing I've found to vinge's
programmer archaeology. There's a lot of useful stuff in there, named / hidden
behind the ugliest incantations imaginable in C and often platform specific or
otherwise buggy.

------
azhenley
I wasn’t very interested in systems level stuff until the last few years with
this new wave of systems languages. I’m itching to learn Zig whenever I get
some free time!

I know most people will say Go isn’t a systems language but it certainly
taught me to think differently than when I use C# or Python.

~~~
_nhynes
Yep. With all of the PL research that has gone into Zig, Nim, Rust, and
others, one would really hope that greenfield projects would be written in
not-C. Of course, when does anyone really get the luxury of writing something
completely from scratch--especially with low-level systems? Maybe someday!

~~~
ajxs
Low level system development is very common in the embedded systems domain!
There's always been a large corpus of development done on embedded systems in
Ada, and there's quite a bit of interest lately in low-level development in
Rust for microcontrollers. As well as interest in using Rust/Ada for operating
system development.

~~~
pjmlp
Including Basic, Pascal and Oberon as well.

------
nercury
It is worth learning C because there is a lot of code written in C.

~~~
anaphor
You can learn enough C to leverage existing code, and then use the FFI
facilities that many languages provide to interface with it. That way you
don't have to engineer your entire application with C. I think this is pretty
common practice, but maybe some people don't realize how common it is that
their, e.g. Python, libraries are leveraging C code.

~~~
adrianN
Somebody has to learn enough C to find a fix security issues.

------
forrestthewoods
Every JavaScript/python programmer that writes horrifyingly slow string/array
code that needlessly allocates should be forced to take remedial C.

------
verisimilitudes
I disagree. The C programming language is directly responsible for countless
damning flaws in modern software and can be credited for the existence of the
majority of the modern computer security industry.

You can write system software in many languages, including Lisp. For a less
outlandish example, Ada is specifically designed for producing reliable
systems worked on by teams of people in a way that reduces errors at program
run time.

I find it amusing to mention UNIX fundamentals as a reason to learn C,
considering UNIX is the only reason C has persisted for so long anyway. Real
operating systems focused largely on interoperation between languages, not
funnelling everything through a single one; Lisp machines focused on
compilation to a single language, but that language was well-designed and
well-equipped, unlike C.

>Last but not least, because C is so “low-level”, you can leverage it to write
highly performant code to squeeze out CPU when performance is critical in some
scenarios.

It’s actually the opposite. The C language is too high-level to correspond to
any single machine, yet too low-level for compilers to optimize for the
specific machine without gargantuan mechanisms. It’s easier to optimize
logical count and bit manipulations in Common Lisp than in C, because Common
Lisp actually provides mechanisms for these things; meanwhile, C has no
equivalent to logical count and its bit shifting is a poor replacement for
field manipulations. Ada permits specifying the bit structures of data types
at a very high level, while continuing to use abstract parts, whereas large C
projects float in a bog of text-replacement macros.

Those are my thoughts on why C isn’t worth learning, although this is nothing
against the author.

~~~
adrianN
C might not be particularly designed for high performance computing, but there
has been an absolutely enormous amount of work on compilers for C. Due to its
prevalence, even CPU manufacturers take care to design their chips so that C
code runs fast on them. Effectively this makes C one of the fastest languages
around.

The story is similar for Javascript. The language design is fairly terrible,
but thanks to millions of dollars invested into Javascript JITs, it's now one
of the fastest interpreted languages.

~~~
pjmlp
Only to catch up with what was already happening outside Bell Labs world.

C is indeed the JavaScript of systems programming.

"Oh, it was quite a while ago. I kind of stopped when C came out. That was a
big blow. We were making so much good progress on optimizations and
transformations. We were getting rid of just one nice problem after another.
When C came out, at one of the SIGPLAN compiler conferences, there was a
debate between Steve Johnson from Bell Labs, who was supporting C, and one of
our people, Bill Harrison, who was working on a project that I had at that
time supporting automatic optimization...The nubbin of the debate was Steve's
defense of not having to build optimizers anymore because the programmer would
take care of it. That it was really a programmer's issue.... Seibel: Do you
think C is a reasonable language if they had restricted its use to operating-
system kernels? Allen: Oh, yeah. That would have been fine. And, in fact, you
need to have something like that, something where experts can really fine-tune
without big bottlenecks because those are key problems to solve. By 1960, we
had a long list of amazing languages: Lisp, APL, Fortran, COBOL, Algol 60.
These are higher-level than C. We have seriously regressed, since C developed.
C has destroyed our ability to advance the state of the art in automatic
optimization, automatic parallelization, automatic mapping of a high-level
language to the machine. This is one of the reasons compilers are ...
basically not taught much anymore in the colleges and universities."

\-- Fran Allen interview, Excerpted from: Peter Seibel. Coders at Work:
Reflections on the Craft of Programming

------
saagarjha
I tend to treat C like English: you don't _have_ to learn it, and you can
certainly get by without it if you stay within a certain semi-isolated
community where it's not really used. However, it is certainly something
that's a nice thing to know, if for nothing else that a lot of _other_ people
will know it too.

------
FraKtus
For me, the advance in thread and memory sanitizers in XCode made it much
safer to use C. Its almost fun to create memory leaks now and see the
sanitizer spotting them :-)

~~~
dingo_bat
Xcode has its own C memory sanitizers? That's very surprising to me.

~~~
saagarjha
They're part of the LLVM project.

~~~
comex
Yeah, Xcode is ‘just’ passing -fsanitize=foo flags to Clang and putting a GUI
on top of the error messages that the sanitized binaries output.

Whether you’re using Xcode or not, in my experience Clang’s sanitizers are
enormously helpful. They’re also available in GCC…

------
adamnemecek
The main problem with C and C++ is the development model. There is a standard
committee that meets for a couple of days three times a year. At this meeting,
they discuss proposals. They have been talking about ranges since maybe around
2000 and they still aren't quite in the language. It would be impossible for
me to contribute to any standard even if I wanted to.

Rust's development happens all on github. I can see all the discussions as
they are happening and potentially even meaningfully contribute. Rust already
has essentially all the things the C++ standard committee wants to standardize
in the next couple of years and then some more.

Besides the language itself, the lack of a package manager is a huge hurdle.

Is this about knowing C or developing in C? Nothing wrong with knowing C, but
developing in C is a bad idea (also immoral /s).

~~~
anaphor
How exactly do you learn C if you aren't supposed to be writing software in
it?

~~~
adamnemecek
I mean write some software in it but like...don't overdo it.

------
valzam
I don't have a traditional CS background and learned several high level
languages (interpreted and compiled) before buying Learn C the Hard Way
earlier this year. I admittedly stopped short of implementing the larger
projects at the end of the book (got too frustrated with the Makefile setup
constantly breaking in weird ways for bigger projects...) but I still think I
got a lot of value of out implementing some basic algorithms like lists or
hashmaps from scratch and having to deal with how memory is used exactly. I
agree with the sentiment that C is syntactic sugar for assembly. I would never
date to write anything non-trivial in C but can highly recommend anyone to at
least spend a couple of months fighting with it.

------
enriquto
While C is undoubtedly necessary as a systems programming language, where it
really shines is in scientific computing. All the modern scientific stacks (R,
julia, numpy, torch, tensorflow, octave, ...) are based mostly on C and, to a
lesser extent, on Fortran.

~~~
pjmlp
Actually C++.

~~~
enriquto
true, a part of this is unfortunately C++

~~~
pjmlp
Anyone that cares about safety sees it as fortunely, actually.

------
lprd
I don't do any systems programming, but I think it would be valuable to learn
C for educational purposes. Could I get some recommendations on books or
online material that could get me started?

------
mastazi
> Especially with Go and Rust go viral right now

Is Go really a system programming language? Are there any notable examples of
projects made in Go that can be considered system programming?

~~~
hu3
Author refers to system programming as a broad term. Not exclusively kernel
and drivers.

For example:

> Regardless of you a system language programmer, DevOps, performance engineer
> or wear other hats, the more you know about the Operating System, the more
> you can do your job better. Take all prevailing Unix-like Operating Systems
> as an example, from kernel to command line tools, they are almost
> implemented in C.

~~~
mastazi
Yes I think you are right, it’s a bit of a different interpretation of the
term I guess

------
meitham
>>> Especially with Go and Rust go viral right now, C seems already forgotten
by people.

I hope the Author realise that golang isn't a system language!

~~~
langitbiru
In the past, I got impression that Go is a system language because how people
"bragged" about Go's performance. Then someone reminded me that Go is in the
same basket with Java, not C++.

