However, when you write and read real-life C code, you have a chance to learn how software works under the hood much better than with higher-level languages.
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.
> struct stuff{ int a; int b; }; Doesn't teach me anything about memory layout.
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.
> The only thing C has going for it is simplicity.
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.
FORTRAN is simple, for instance. Yet we don't use it much any more.
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.
I've done macro assembler. (Hell, I've written a couple). I know all about macros. C is not just a fancy macro assembler.
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.]
Indeed and even more... The entire GEOS operating system, gui/windowing system, application suite, and the vast majority of the rest of the apps were written in 8086 assembly language and ran on an original IBM PC.
There are a few niches where new code is written in assembler (numerical code, parts of standard libraries), but in general, I think 'maintained' is a better word to use.
I don't think you have to go all the way back to the ST,Amiga era, I recall there was a surge of Win32 assembly after that with some nice assembly applications as the result. However with the level of optimization offered by compilers today, these days assembly seems mainly relegated to where it's fine-grained control allows for better performance in extremely performance oriented parts of code. An example of such would be SIMD code where highly fine-tuned assembly code often runs circles around the compiler generated equivalent, as proven by simply comparing x264 performance with or without assembly optimizations.
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.
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.