Earlier quoted context omitted.
"Exploding" undefined behaviour is a problem created by C. Other languages don't have it, not even assembly languages.
There are many languages which have undefined behavior. C does have a lot of it, and it can be anywhere. In many languages, you can cause undefined behavior through its FFI. Some languages, like Rust, have UB, but only in well-defined places (a module containing unsafe code, in its case).
Should you learn C to “learn how the computer works”?
311–320 of 381 posts
Re: Should you learn C to “learn how the computer works”?
#312Earlier quoted context omitted.
You're joking. There's a very serious distinction between the stack and the heap - perhaps they live in the same memory but they are used very differently and if you mix them up your things will break.
There is no hardware distinction between stack memory and heap memory. In fact C teaches a model of a semi-standard virtual architecture - loosely based on the DEC PDP7 and/or PDP11 - which is long gone from real hardware. Real hardware today has multiple abstraction layers under the assembly code, and all but the top layer is inaccessible. So there's no single definitive model of "How computers work." They work at w…
Re: Should you learn C to “learn how the computer works”?
#313I teach C. It's an increasingly terrible way of learning "how a computer works". You would be much better off learning a little assembler. The problem is most of the pain of learning C comes from undefined behaviour, which isn't how "computers work". The fact that depending on optimisation level writing past the end of an array might write to memory, or might not, is (mostly) a unique feature of C. Similarly sometime…
You get that, if that's what your CPU implements. Of course that's what all commonly used processors nowadays like x86 do. But C was meant to run on weirder ISAs as well. The specs of C allow so much undefined behavior in order to let C just emit the instructions for the multiply or memory access, and C deliberately says "not my problem" for whatever this architecture happens to do with edge-cases like overflow.
Using assembly instead of C cuts past the abstraction of the architecture, for both good and bad. You get defined behavior but lose portability. Practically speaking, yes, x86 assembly probably is a better way to learn without getting distracted by forty-year-old hardware oddities.
Re: Should you learn C to “learn how the computer works”?
#314Earlier quoted context omitted.
IIRC there was some UB feature that when compiled with GCC would launch nethack.
Found it: >When GCC identified “bad” C++ code, it tried to start NetHack, Rogue, or Towers of Hanoi. Failing all three, it would just print out a nice, cryptic error message. https://feross.org/gcc-ownage/
Re: Should you learn C to “learn how the computer works”?
#315Earlier quoted context omitted.
C at the very least teaches the difference between stack and heap memory, a crucial concept obscured by most higher-level languages.
There is no difference inside the computer between those concepts. For convenience, many architectures have a single assembly instruction for taking one register (called the "stack pointer"), adjusting it by a word, and moving a register to the address at that word, and a corresponding instruction to move data back to a register and adjust it in the other direction. That's the extent of the abstraction. You can imple…
Re: Should you learn C to “learn how the computer works”?
#316Earlier quoted context omitted.
I suspect that by "virtual machine" he means "the C abstract machine". There's arguably little relevant difference between an abstract machine and a virtual machine—for example, Android has an AOT compiler for the Java "virtual machine".
To be exceedingly clear about it: yes, that is my fundamental point.
Re: Should you learn C to “learn how the computer works”?
#317Re: Should you learn C to “learn how the computer works”?
#318C teaches you how a computer works because the C abstract machine is defined such that operations that must be manually performed in assembly language must also be manually performed in C. C doesn't let you write something like: string x = y; ...because to actually create a copy of a string the computer must allocate memory, copy memory, and eventually free the memory. In C each of these steps is manual. Higher-level…
Well... C teaches you how a PDP-11 worked, but modern computers aren't PDP-11s either. Most happen to expose a PDP-11-like structure via x86 assembly, but even that abstraction is a bit of a lie relative to what's going on under-the-hood. C doesn't let you write "string x = y" because it doesn't have string as a primitive variable type; that's the whole and only reason. It's not quite correct to say that "the compute…
Re: Should you learn C to “learn how the computer works”?
#319Earlier quoted context omitted.
C at the very least teaches the difference between stack and heap memory, a crucial concept obscured by most higher-level languages.
Here's the C99 standard: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf There are no occurrences of the words "stack" or "heap" in this document. What the spec actually discusses is "storage durations". Now, in many cases you can say, well, "automatic storage duration" means it's on the stack, but that's not something C has any opinions about. If you want to know about the stack and the heap, saying "learn…
I'm not even sure the C memory model depends on the code/stack/heap separation.
Re: Should you learn C to “learn how the computer works”?
#320Earlier quoted context omitted.
To be exceedingly clear about it: yes, that is my fundamental point.
But, I mean, how useful is that point? Virtually everything, including the X86 ISA, is an "abstract machine".
The first reason that this is useful is that there are a lot of people out there who believe that C is somehow fundamental to computing. You and I both know that this is false, but I run into a ton of people who don't understand this. So, this first post is setting that stage: everything is always an abstraction, in the big picture sense. That's bullet point one.
That said, interpreting people literally isn't a good way to have a conversation, you need to know what they are saying, which may or may not connect to the exact words they used. I think when people are saying this phrase, they don't mean it in a literal sense. Part of the reason why is that there are some differences that matter when you zoom in from the big picture. I speculate a bit as to why, but regardless, knowing that it may not be literal is point two.
I've heard "all models are wrong; some models are useful" attributed to several people, but it's sort of the counter argument to bullet point one, and so makes up bullet point three. Drawing a distinction between the C abstract machine and the JVM can be a useful mental model, even if it's incorrect in some sense. The C abstract machine is closer to hardware than the JVM is, and even if it's not a perfect mapping, you'll be exposed to stuff that's closer than your high level language. As long as you know you're still working with an abstraction, learning C can be a great way to be exposed to this stuff. Just keep in mind that it's not magic, or particularly inherently special.
I do think that this is useful on its own. If you reduce it down to a soundbyte, sure, that's not interesting, but the interesting thing is in the details. In some sense, this is kind of the fundamental point of the article. You may find that boring, but that's okay; this stuff isn't really for you, both in a literal and figurative (experienced C developers generally) sense.
------------------------------------
Part two is going to discuss what happens if you take this to an extreme. I have two small bits of sample code that fundamentally do the same number of operations, and have the same computational complexity, but one runs much, much faster. This is due to how it interacts with caching, which is not part of the C spec, but is the reality of x86 (at least) hardware. This exposes a sort of fundamental tension when thinking about how the abstract machine relates to the physical machine. This is where C is really interesting, because it's low level enough to allow you to control memory allocation and access, which higher-level language users aren't really exposed to. This is the "why is this useful," really. The task is to know what behaviors you can rely on and which ones you can't, and how it relates to the hardware you actually want to support.
------------------------------------
Part three is going to show what happens if you make a mistake with the ideas from part two. If you incorrectly assume that C's abstract model maps directly to hardware, you can make mistakes. This is where UB gets dangerous. While you can take advantages of some properties of the machine you're relying on, you have to know which ones fit within C's model and which ones don't.
------------------------------------
I split this into three parts because it's an MVP, in a sense. Ship the first part so that I don't continue to revise the beginning over and over and over again. They're all related to each other, and could be one whole work, but it's true that this one is less directly useful than the others, as it's really setting the stage.
... does that all make sense?