11 ms·
C doesn't necessarily teach you how computers work. But it does teach you how software works. Our modern software empire is built on mountains of C, and deferen
by Sir_Cmpwn 8y ago
C doesn't necessarily teach you how computers work. But it does teach you how software works. Our modern software empire is built on mountains of C, and deference to C is pervasive throughout higher-level software design.
>You may have heard another slogan when talking about C: “C is portable assembler.” If you think about this slogan for a minute, you’ll also find that if it’s true, C cannot be how the computer works: there are many kinds of different computers, with different architectures
As with many things, this phrase is an approximation. C is a portable implementation of many useful behaviors which map reasonably closely to tasks commonly done on most instruction sets. C programmers rarely write assembly to optimize (usually to capture those arch-specific features which are unrepresentable in C), but programmers in other languages often reach for C to write the high-performance parts of their code. The same reason C programmers in the early days would reach for assembly is now the reason higher-level programmers reach for C.
- ashleyn 8y agoC at the very least teaches the difference between stack and heap memory, a crucial concept obscured by most higher-level languages.
- flyinglizard 8y agoOr passing by value vs passing a pointer, or the importance of memory management... Modern languages solve problems created developing in legacy languages (primarily C). The issue is that knowing a solution without knowing the prior problem which the solution addresses, doesn't really lend itself to clarity.
- Sir_Cmpwn 8y agoThey don't solve problems created by C, they just solve problems C doesn't. The problems still exist, and sometimes the high-level compiler even picks the wrong solution. Learning C helps you understand when and why this has happened.
- flyinglizard 8y agoArguably those problems did not exist before the C-era as software wasn't large or complicated enough. C++ directly attempts to improve over C by providing new mechanisms to solve problems encountered by C developers every day. Point is that out of context, a lot of the things modern languages provide seem superfluous. Why have objects? Why have templates, closures, or lambdas? For a novice programmer, these are many answers for questions yet to be asked. When you come from a heavy C background and you encounter something like templates, you know EXACTLY what this is for and wish you had it years ago. I'm as hardcore as they get C programmer and I'm having a ball with C# for this very reason.
- Sir_Cmpwn 8y agoWhen I see templates, I run in the opposite direction at a dead sprint. C++ has added very little of value to the language and templates are one of their worst offerings. Metaprogramming is an antipattern in C.
- sevensor 8y ago> Metaprogramming is an antipattern in C. Yet we often see passionate arguments in its favor, as for instance from Sústrik (http://250bpm.com/blog:56 http://250bpm.com/blog:56) and Tatham (https://www.chiark.greenend.org.uk/~sgtatham/mp/ https://www.chiark.greenend.org.uk/~sgtatham/mp/) and a person on the internet who recommends replacing LAPACK with preprocessor macros (http://wordsandbuttons.online/outperforming_lapack_with_c_metaprogramming.html http://wordsandbuttons.online/outperforming_lapack_with_c_me...). Would you care to comment on its enduring popularity?
- Sir_Cmpwn 8y agoYou can write bad code in any language, C is no exception. We also see people persistently arguing in favor of simpler and less magical C. If you want reliable, maintainable code in any language, do not use magic. Metaprogramming is cool, no doubt about it, but it's unnecessary and creates bad code.
- sephoric 8y agoMemory management in C is very different than memory management in JavaScript, which throws exceptions when you try to access invalid memory, but will gladly let you create memory leaks inside event callbacks or other loops and keep chugging along gladly until it can't anymore. Writing a safe, efficient, high performing JavaScript app is very different than writing the same thing in C, and although I know both deeply well, I don't think learning C has helped me write better JS.
- oleganza 8y agoAfter recently (1 year) learning Rust I noticed that Rust teaches about low-level CS concepts of passing by value vs passing by pointer a lot better than C. Because you not just have references and values, but also have to think about richer semantics of what you are trying to do: sharing, mutability, and move semantics. So you learn not only that a few low level concepts exist, but you also learn how to use them correctly.
- jreck 8y agoYou can also learn that difference in something like C#. You don't need C for that. And C pretends there's a distinction between the stack & heap that doesn't actually exist. There is no significant difference there.
- Sir_Cmpwn 8y agoYou'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.
- rrauenza 8y ago...like returning a pointer to the stack: char *dupstr(const char *src) { char new[1024]; strlcpy(new, src, 1024); return new; }
- TheOtherHobbes 8y agoThere 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 whatever level of abstraction you need them to work. You should definitely be familiar with a good selection of levels - and unless you're doing chip design, they're all equally real.
- t0mbstone 8y agoJust playing devil's advocate here... why is this such a "crucial" concept, for someone who is using a higher level language (like Python or Ruby)? If 99% of what that person does is gluing together APIs and software modules, and they can see their memory usages are well within range, why does it matter?
- pq0ak2nnd 8y agotrue. it's like interior decorator vs. interior designer if you're just going to move couches around and put down throw pillows, who cares about a solid foundation of design fundamentals and architectural psychology.
- mturmon 8y agoYep. In truth, one can almost never win the argument of, "...but do I really need to know this?" But people should be aware that knowing fundamental things provides insight and advantages.
- greenshackle2 8y agoFor sure, but there are diminishing returns. My rule of thumb is that understanding two levels of abstraction below whatever you're doing is usually worth it.
- gowld 8y agoIt's also like fluid dynamicist vs quantum physicists. If you're just going to move oil around and put down differential equations, who cares about a solid foundation of quantum gravity and chromodynamics.
- danso 8y agoI can’t make a thorough argument, but u/flyinglizard (in a comment adjacent to mine) mentioned what I was thinking: Ruby/Python almost entirely abstracts the concept of pointers, references, and memory. For beginners and intermediates, it’s not a big deal until you get mutable data structures like lists and dicts. When I learned CS, we were taught C/C++ first, and I remember the concept of mutability and object identity being not at all difficult. Without being able to refer to fundamental structures and concepts, I usually just exclusively teach students non-mutable patterns (e.g. list.sorted vs list.sort), while pushing them to become much proactive at inspecting and debugging.
- aikah 8y ago> C at the very least teaches the difference between stack and heap memory, a crucial concept obscured by most higher-level languages. Go does that too. C teaches manual memory allocation, de-allocation and pointer arithmetic as well.
- xfer 8y agoNo, it doesn't. Go does escape analysis and allocates stuff on heap if needed.
- majewsky 8y agoNo. In the machine model used by Go, everything is allocated on the heap. The optimizing part of the compiler can then move allocations from the heap onto the stack if it can prove that the allocation does not escape the lifetime of a stack frame.
- geofft 8y agoThere 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 implement it with two instructions instead of one, and then you get as many stack pointers as you want. Zero, if you want. There is no "heap" on modern OSes. There used to be a thing called the program break, beyond which was the heap. It's almost meaningless now. You ask for an area of virtual memory via mmap or equivalent; it is now one of several heaps you have. And you can pass around pointers from all your stacks and all your heaps in the same ways, as long as they remain valid. You need to understand this in order to understand how thread stacks work (you typically allocate them via mmap or from "the heap"); how sigaltstack works and why you need it to catch SIGSEGV from stack oveflows; how segmented stacks (as previously used in Go and Rust) work; how Stackless Python works; how to share data structures between processes; etc.
- jpindar 8y agoFor your definition of 'computer', perhaps. Many microcontrollers have the stack defined by the hardware.
- simonh 8y agoLeaning how microcontrollers work is not the same as 'learning how the computer works' in general, which is the subject at hand.
- geofft 8y agoAnd some, such as RISC-V, have no predefined stack pointer at all. (Push and pop are implemented by doing them manually; call is implemented by the jump instruction optionally taking an argument for a register to use as the stack.) C, at best, teaches you the C abstract machine, which supports both of these concrete implementations equally well.
- tptacek 8y ago
- XMPPwocky 8y agoHere's the C99 standard: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf 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 C, and then learn how these abstract C concepts map onto the stack and the heap" might not actually be the best way to figure this stuff out. What actually ends up happening, in my experience, is that to figure C out you have to get a decent mental model of how the stack and heap work, and then, from that, say "automatic storage duration and malloc/free are basically just the stack and the heap". I learned calculus as a kid because I needed it for an online electronics class I was taking. But I'm not going to recommend that folks take electronics classes so they learn calculus! (that said- having a motivation to learn something, having an application in mind, a problem you want to solve... certainly seems to make learning easier.)
- gdwatson 8y ago"C" doesn't just include the spec but also the customary semantics [1] of the language. The customary semantics certainly include the stack and the heap. [1] https://arcanesentiment.blogspot.com/2014/12/customary-semantics.html https://arcanesentiment.blogspot.com/2014/12/customary-seman...
- drb91 8y agoPeople don’t learn C by learning the spec, and virtually every c implementation and runtime I know, it uses a stack and heap. Oddly you seem to recognize this, so I’m not sure what your point is.
- bunderbunder 8y agoAt some point, these conversations seem to always turn into a contest to see who can be the most pedantic. Which I think is pretty fun - and pretty well in the spirit of the original article - but that's me, and I can't fault anyone for being turned off by it. 's funny, I am certainly aware of having, at some point, at least skimmed over the C99 standard encountered the concept of a storage duration. But, aside from that brief few hours, virtually the entirety of my C-knowing career has been spent thinking and talking in terms of stacks and heaps. Meanwhile, in my .NET career, I really did (and, I guess, still do) feel like it was important to keep track of how .NET's distinction is between reference types and value types, and whether or not they were stack or heap allocated was an implementation detail.
- scott_s 8y agoI don't disagree with anything you said, but I wanted to point out you also don't disagree with the author. Your point ("this phrase is an approximation") is also the author's point. I felt he walked through the why fairly and with nuance.
- jcelerier 8y ago> there are many kinds of different computers, with different architectures are there ? how many laptops, desktops, smartphones, tablets, use anything else than the von neumann architecture ?
- hsivonen 8y agoOn hardware level, all those use von Neumann architecture. Wasm, however, exposes Harvard architecture (implemented on top on von Neumann machines). Yet, you can compile C to Wasm, which is a data point about how abstract C is: so abstract that it can target Harvard architecture despite targeting just von Neumann architecture being sufficient for targeting modern hardware.
- esmi 8y agoIf only things were so simple... https://en.m.wikipedia.org/wiki/Modified_Harvard_architecture https://en.m.wikipedia.org/wiki/Modified_Harvard_architectur...
- fixermark 8y agoIt varies from application to application. In my domain, we reach for the CUDA libraries to write the high-performance parts of our code. ;)
- iforgotpassword 8y agoThat requires special Hardware in contrast to C code.
- fixermark 8y agoI spent like a thousand dollars on the box sitting under my desk; I'm pretty sure my C code runs on special hardware too. ;) (and worth noting: if I pull out the special hardware you're thinking of from that box, my particular thousand-dollar-box is no longer able to run software I need because the GUI requires a graphics accelerator card. The OS authors have already reached for a subset of CUDA to optimize the parts of the GUI that needed optimization).
- pcwalton 8y agoIntel will not sell you an x86 processor without a GPU capable of compute these days.
- iforgotpassword 8y agoThat will affect a raspberry pi user how?
- fixermark 8y agoRaspberry Pi has a Videocore IV GPU on the chip.
- pcwalton 8y agoOur popular software stacks are written in C and C++, but that's more because of history than anything else. I rarely reach for just "classic" C for performance anymore. These days I'm more likely to reach for GPUs, SIMD intrinsics (available in Rust), or at least Rust/Rayon for code that needs maximum performance. C is fundamentally a scalar language. In 2018, the only code for which "classic" C is the fastest is code that exhibits a low level of data parallelism and depends on dynamic superscalar scheduling. Fundamentally sequential algorithms like LZ data compression, or very branchy code like HTTP servers, are examples. This kind of code is becoming increasingly less prevalent as hardware fills in the gaps. Whereas we used to have C matrix multiplies, now we have TPUs and Neural Engines. We used to have H.264 video codecs, but now we have huge video decoding blocks on every x86 CPU Intel ships. Software implementations of AES used to be the go-to solution, but now we have AES-NI. Etc. As an example, I'm playing around with high-performance fluid dynamics simulations in my spare time. Twenty years ago, C++ would have been the only game in town. But I went with TypeScript. Why? Because the performance-sensitive parts are GLSL anyway--if you're writing scalar code in any language, you've lost--and the edit/run/debug cycle of the browser is hard to beat.
- Thaxll 8y agoAre you promoting Rust on every topic about GC / performance topics?
- pcwalton 8y agoRust is fundamentally a scalar language too. If anything, I'm promoting vector languages like GLSL/Metal/etc.
- btmorex 8y agoExcept even when you think vectorized processing should be a performance win, it often isn't: http://www.vldb.org/pvldb/vol11/p2209-kersten.pdf http://www.vldb.org/pvldb/vol11/p2209-kersten.pdf I'd argue that GPUs and SIMD instructions have so many restrictions that they're useless for general purpose computing. Yeah, they have niche spaces, but in terms of all the different kinds of programs we write, I think those spaces are going to remain niche.
- geofft 8y ago> Our modern software empire is built on mountains of C, and deference to C is pervasive throughout higher-level software design. This I agree with and also I'd call the most important reason for Rust programmers to learn C. The C ABI is a lingua franca, not because it's good or pure but (as with spoken linguas franca) happened to be in the right place at the right time. It defines certain conventions and assumptions that didn't have to be assumed (implicit stack growth without declared bounds, single return value, NUL-terminated strings, etc.) and a lot of software is written to it, to the point that if you want your (e.g.) Python code and Rust code to interoperate, the easiest way is to get them to both speak C-compatible interfaces, even though you're not writing any actual C code.
- ElijahLynn 8y agoFor those who don't know what ABI means (as I did not) and thought it might be a typo. ABI = Application Binary Interface https://en.wikipedia.org/wiki/Application_binary_interface https://en.wikipedia.org/wiki/Application_binary_interface https://upload.wikimedia.org/wikipedia/commons/b/bb/Linux_API_and_Linux_ABI.svg https://upload.wikimedia.org/wikipedia/commons/b/bb/Linux_AP...
- geofft 8y agoYup. In compiled-languages land it's common to see things that are API-compatible but not ABI-compatible: the compiled objects don't work together, but if you recompiled the same source code it would work with no changes. This happens (sometimes) when you do things like reorder members of a structure or switch to a larger integer type for the same variable. C and libc have been ABI-stable on most platforms for decades. C++ on some platforms (e.g., GNU) is stable with occasional ABI breakages in the standard library; on others (e.g., MS Visual C++) it breaks with new compiler versions. Rust isn't ABI-stable at all and has no clear plans for it, despite a strong commitment to API stability (i.e., old code will compile on new compiler versions). Swift 5 is targeting ABI stability.
- Sir_Cmpwn 8y agoTo save you the read: in web terms, the API is e.g. Stripe and the ABI is HTTP.
- hindsightRegret 8y ago> Our modern software empire is built on mountains of C, and deference to C is pervasive throughout higher-level software design. this is the key. If you want a real-world view into how the kernel is written (i.e. more of the computer), then C is the only practical choice. Sure, you can write an OS in Java, Haskell, OCaml, etc. But it's roundabout. So, I think the statement, "learn how a computer works more by leveraging C" is valid.
- xtracto 8y agoExactly, if you want to know how a computer works, you should take a course in Computer Architecture. I remember that subject from BSc quite fondly.
- discreteevent 8y agoExactly. Maybe we could say that for 99% of software C is the lowest level. Its implemented in something, that's implemented in something, that's implemented in C. So, if you learn it, you can understand your software stack completely. Also, C is learn once, write anywhere. If there is some kind of computer, then there is probably a C compiler for it. That's not true of any other language to the same extent. Know C and you can program anything.
- philwelch 8y agoC is a useful and universal model of computation not just because most native software is ultimately built on C, but because any hardware that people end up widely using also has to have a sensible C compiler. The C model is relatively close, I’ve heard, to the hardware of a PDP-11. But there have been tons of abstractions and adaptations on both sides of that coin ever since; not only can a C program provide a language runtime that behaves quite differently from a PDP-11, but the C program itself is compiled to machine code that is sometimes quite different than the code for a PDP-11, and even the low level behavior of the CPU often differs from what’s implied by the ISA. And while all of these models of computation are Turing equivalent, the transformations to and from C are very well known.