To understand how a computer works when it executes a program, accesses some data, writes to a disk, spawns a process and forks it, then kills the child after a certain interrupt can pretty much only be learned by learning assembly and some hardware and OS-related literature. Of course, for most programmers(let alone people in general) this is nearly useless information today. It's obsolete. And that's good.
But what C teaches you about how "things are under the hood", is just a leaky abstraction. C doesn't care about such low-level things because they are implementation details. Standard C could be ran by pen and paper just as well as on a computer. It's a "black box". Takes input and produces output according to the standard.
Frankly, the only "low-level" thing C has over e.g. Java is that most domains in which C is used are working close to the hardware and it runs on "the real hard machine". With C, in it's modern domains, you are exposed to these "low-level" details, not because of C, but because of the domain. With many other languages the user doesn't have to deal with such problems because of the problem domain is different.
I'm not sure how you can say that knowledge of assembly/hardware is obsolete. Perhaps unneeded for tasks that most programmers have in our hip new ad-driven world, but someone still has to understand and write the code that runs the code that eventually puts the cat-based meme on your screen.
Engineers seldom use calculus, but they used derived functions every day. The same applies to programming, everyone who programs should be exposed to the low level innards enough to at least take the magic away and have a basic concept of what a computer actually does. You'll be better for it.
That's funny, because 97% of the Linux kernel (the code that implements the things you mentioned) is written in C.
I think you're getting confused by the fact that C has a standard library, so it's true that you don't have to write process spawning yourself. But the standard library is written mostly in C and calls into an OS that is written mostly in C. So C's computational model can indeed explain how low-level things work.
The C standard even defines a "freestanding implementation," which is a C platform that has no access to an OS. So C-the-language can work without any OS at all, and without any runtime/interpreter that is filling the role of a traditional OS. Very few languages can say that, and indeed it's the reason why it makes sense to develop an OS in the language. To write an OS in a language that requires an OS begs the question.
As for process spawning, I/O, etc.: it might be that C is just as good as assembly for those, but I would be surprised if either was sufficient. Presumably that's where "hardware and OS-related literature" comes in.
Damn right it's good--for me--that most programmers think low-level programming/machine knowledge is obsolete. I'm pretty certain it means I'll never want for a (good) job as long as I can still think and move enough to edit code.
getchar(); //C STDIN.getc //Ruby getChar //Haskell System.in.read(); //java How do any of those show me how software works under the hood? In none of them do I have any clue how a character makes it from my terminal to my program.
int* a; Teaches me nothing about caches, memory latency, NUMA etc. Hell, dereferences are even guarantied to read the same physical location in memory(just the same logical location.)
struct stuff{ int a; int b; };
Doesn't teach me anything about memory layout. C assumes you are running on some hardware from the 70s, it doesn't know about virtual memory, address spaces, memory pages, NUMA, ram with multiple channels, the no execute bit, GPGPU programming. The only thing C has going for it is simplicity.
Maybe not, but this does: offsetof(struct stuff, a); offsetof(struct stuff, b); sizeof(struct stuff);
> C assumes you are running on some hardware from the 70s, it doesn't know about virtual memory, address spaces, memory pages, NUMA, ram with multiple channels, the no execute bit, GPGPU programming.
I'm not sure what you're complaining about; virtual memory is explicitly designed so that no code (even assembly language) "knows" about it except for the very small amount of code that sets it up. Likewise with most of the features you are mentioning. A memory reference in C operates at about the same level of abstraction in C as it does in assembly language, which is the lowest-level software interface available.
But it's an interesting /kind/ of simplicity.
FORTRAN is simple, for instance. Yet we don't use it much any more.
C is simple enough to not make you go bananas trying to learn the language [C++], but rich enough that you don't go bananas solving large, interesting problems [assembly]. It pretty much nailed the uncanny valley of just complex enough.
It's a bit creaky. It desperately needs namespaces. I'm on the fence about memory models (this is a platform thing, in my mind), and definitely Do Not Want threads jammed into the language. I would love a decent macro language, but that's probably a decade-long debate (if what happened in the Scheme community is any indication). I would love a compilation system that didn't suck [yes, macros and a Go-like build system probably don't mix well].
I've been writing C for over 30 years. I plan to keep writing it for another 20. The unscientific, neat thing about C is that it's /fun/. I know this doesn't go over well with standards types and cow-orkers who feel the urge to override operator = and take dependencies on Koenig lookup all the time, but C has a charm that other, similar languages have been unable to capture.
Computing is like an iceberg, with the web being the bit above the surface.
rich enough that you don't go bananas solving large, interesting problems [assembly]
Two words: macro assembler. It may surprise you to know that not that long ago, sophisticated GUI apps were written in assembly language (in fact the IDE I use on my ST, Devpac, was written in assembly, and it's everything you would expect of a modern IDE - editor, compiler, debugger, etc - running under GEM). Many games were written in pure ASM.
This doesn't get you away from the ooky stuff that C does for you, like register allocation and code optimization (link time code generation is a wonderful thing, even for C). Very few assmeblers are bright enough -- or should be trusted enough -- to do code motion, strength reduction, common subexpression analysis. And, oh bog, just writing a plain expression? Not even possible in a macro assembler.
[I rewrote the ST's file system in assembly, btw. It started out as an honest effort, then got bogged down in stuff that would have been a no-brainer in C.]
Personally I haven't done any assembly programming in atleast the past 6-7 years but I still get the urge now and then to program in it again. However, even though it's unlikely that happens, my assembly experience has given me a thorough understanding of how the computer works at a basic instruction/memory level which has been extremely valuable when I want to create optimized code in higher level languages (like C). So yes, while learning C is certainly worthwhile even if you are going to write in even higher level languages, learning or atleast graping the fundamentals of assembly is in my opinion even better.
Why have a type system that includes implicit coercions between types, sometimes with different internal representations? (Just imagine an int promoting to a float.) Doesn't that obscure the "real meaning" of the program, or is it merely a detail you find uninteresting? C is not the only language that makes these low-level concepts accessible, and C does not represent the "floor" with respect to making the behavior of hardware explicit.
If what you're actually getting at is that C is the most popular language today that exposes all of those things, it sounds less convincing- Systems languages cannot advance if we take C's position as given.
This isn't less convincing to me, I would never argue that someone learn C instead of all other languages. But I do believe that knowing C makes you a better programmer in all (current popular production) languages.
Re: your point about systems programming not advancing...I didn't mean to imply that C was "perfect" (in the Latin sense, meaning "done, finished"), just that it's the best we have for many things.
I do really like Go, and wish I could use it more, for non-personal projects.
You've never written C.
Well yes, but you're talking about library code there. Your code, which links in libraries, will never fully describe those libraries.
But that library code was very likely written in C...so...
If you like C, use it. If you want to know what's going on, learn assembly.
If you know C, it is possible to comprehend a direct mapping of your "high level" code to a set of processor instructions. This is complicated by modern huge glib-style libraries and modern compiler optimizations, but it remains possible.
That, combined with the fact that C is foundational in almost all current production languages, and remains highly useful among them, are why I think anyone who takes programming seriously should know C.
Someone else compared C to Latin. Obviously C is more useful on its own than Latin, but the comparison is valid for the foundational aspect. Understanding threads and locking in C gets you a long way toward understanding those concepts in most popular languages.
I'd make the same arguments for understanding Unix, by the way...and also that you can't really understand Unix without understanding C. And the converse.
I disagree. It's complicated by those things, but more importantly it's complicated by modern processor architectures, which do not execute one instruction after another anymore like C would have you believe - they just mostly pretend to.
Which isn't to say this is any less true of the other high-level languages we have at hand, and I still think knowing C is valuable (although I'm wavering more on that then I was 5 years ago), I just think your characterization is incorrect.
At that level, even the compiled opcodes aren't strictly demonstrative of what's happening inside the processor, but you'd only know that if you were running a simulator and watching traces light up.
Extensions such as __builtin_prefetch, __builtin_expect are heavily used in the Linux kernel to allow a higher level of optimization by directly instructing the compiler how to handle branch prediction and caching (based upon careful benchmarking) in performance critical areas rather than leaving it up to the compiler's 'compile-time' heuristics.
But, lines of assembly language professionally written: 0.
It's possible to understand the entirety of practical computation from top to bottom without C needing to be in your toolbox. Correct me if I'm wrong.
However, C requires you to know more about how the compiler is going to turn your character string, say, or your looping construct, into assembly code.
Some languages don't even have structs or pointers, or explicit memory allocation! You're laughing (I hope), but these are real things in memory, with only a thin layer of magic (layout, pointer math) in C...whereas they do exist in other languages, but are hidden -- usually pretty well, but rarely completely to the point where you are better off not knowing about them.
And for pointers, I'll leave you with http://en.wikipedia.org/wiki/Tony_Hoare#Quotations
[1] Tho' processors since the Z80 have instructions to help you deal with null-terminated strings...
> However, C requires you to know more about how the compiler is going to turn your ... into assembly code.
But does it really require (with emphasis)? The nature of some debugging and optimisation might push you towards that, but it's still a choice.
I feel your point comes down to the use of C as a "vehicle" to learn low-level concepts. Which is fine, but someone can also just learn them directly (upfront or retroactively) and use whatever progression of languages they want.
Only if the OS was written in C...
- all the OS written before C ever existed, some still in use, even if in legacy deployments
- Symbian (written in C++)
- Mac OS X (is a mix of C++ and C)
- Windows is moving to C++ as the default systems language
- Native Oberon (Oberon)
- Lilitth (Modula-2)
- Spin (Modula-3)
- BeOS/Haiku (Written in C++)
- House (Haskell)
- MarteOS (Ada)
- IBM's OS/400 system (Written originally in Modula-2 and PL/MI)
- Singularity (Written in C# and C++)
- Lisp OS (Assembly and Lisp)
- Smalltalk Machines (Assembly and Smalltalk)
- Forth based OS for embedded systems
So one cannot expect that an OS will be always written in C.
And I have never met a single person who knows any of your remaining languages who isn't at last passably familiar with C. I've never asked, but I don't think any of them would call their C knowledge unimportant or not useful.
OS X and Windows have some C++ (mostly wrappers) but otherwise are C.
OS X drivers are written in C++, they are not wrappers over C.
http://developer.apple.com/library/mac/#documentation/device...
In Windows all the new APIs since Windows Vista are mostly COM (C++) based, including the new Userspace Driver Framework.
With Windows 8 this will increase, as Microsoft is moving to have C++ as their official systems language, by introducing WinRT as the new subsystem, and dropping support for anything besides C89.
Just because C is used a lot, you cannot say that all (even 99,9%) operating systems use it.
Actually I failed to mention the amount of operating systems that you may have running on your watch, vcr, radio, microwave, etc, which summed together are much more that the OS X, Windows, Linux base.
I do like C myself and plan to continue using it. I've been looking in to OCaml recently and as someone else in the comments indicated, they seem like they'd make a good team.
Same even goes for something like C# and Java. And in any event, the major influence on how efficiently code runs is the underlying CPU, not C. C is the same, no matter which CPU it's going to be compiled for; the resulting code, which is what actually gets run, will vary. `p[i]', where `int p[100]' is a global, is going to turn into something quite different when built for MIPS from what it would for x86.
So what good is learning C? Well, if somebody will give you a job with it, and that job is well paid, then maybe it would be worth learning seriously... not sure how common this is these days, though.
Or if you want to hack at and/or understand parts of Linux or any of countless utilities.
If you really want to know how current computers work at low level, then Assembly is the only way.
Everything else are higher level abstractions.
Because if you don't know C, you've only seen the map, not the territory.
Because if you don't know C, you're looking at the finger, not the moon.
Because if you don't know C, everything you think about computers is a leaky abstraction.
The counterpoint is that flawed models are often good enough. Newton's theory of gravitation comes to mind.
But if you like knowing what's going on, either for personal satisfaction, or because what you're working on is not fully encapsulated by a different language (inevitably, originally, written in C), then C is both interesting and useful.