11

I believe C is a good language to learn the principles behind programming. What do you stand to learn in lower level languages that are "magicked" away from high level ones, such as Ruby?

Doc Brown
  • 199,015
  • 33
  • 367
  • 565
Andrew
  • 231
  • 1
  • 4
  • 2
    You learn the magick, or some of it, anyway. Because C is "closer to the metal," you learn more about the metal. – Robert Harvey Aug 17 '16 at 21:59
  • 1
    I never fully understood the concept of reference until I encountered C++. – user6245072 Aug 17 '16 at 22:01
  • 6
    C will teach you what a stack overflow actually is. The hard way. – david25272 Aug 17 '16 at 22:48
  • 1
    I wish new programmers would start by learning good old Pascal and structured programming. You learn to express yourself logically and structured. – Bent Aug 20 '16 at 20:41
  • 1
    Meh, if you want to be close to the metal wire up some damn metal. 555 chips are still fun to play with. If you want to learn to program the metal use whatever. If you want to use the metal to its fullest don't just futz around with `c`. Write some assembly that uses those special op codes on your CPU that `c` never heard of. `c` teaches you do to by hand what `c` doesn't do for you. So does every other language. It's just that there is a lot that `c` doesn't do for you. – candied_orange Aug 25 '16 at 02:19
  • 2
    Something that only C and C++ teach you is that the compiler is your worst enemy. – CodesInChaos Aug 26 '16 at 07:04
  • @david25272 Stack overflows are just as easy to produce in most higher level languages. If you write recursive code on a finite size stack, you're bound to see one at some point. – CodesInChaos Aug 26 '16 at 07:09
  • 1
    I'm a C# kind of guy who's learning C/C++. What has it taught me that C# never could have? *How to appreciate sane and useful compiler errors!* That's top of the list, but it's a general lesson in appreciation for all the things higher level languages do for us so we don't have to worry about it. Like throwing an exception if you try to read passed the end of an array. Other than that, there's nothing C can teach you that you can't learn in a newer language. – RubberDuck Aug 28 '16 at 10:35

7 Answers7

13

I know C is a good language to learn the principles behind programming.

I disagree. C is absent too many features to learn principles behind programming. C's features for creating abstractions are terrible, and abstraction is one of the key principles behind programming.

If you want to learn how hardware works, and hence some mechanical sympathy for the machine, you should learn machine code, aka the instruction set architecture, and also study the cache construction of modern CPUs. Note that I am not recommending assembly language, just understand the hardware instructions, so you understand what a compiler generates.

If you want to learn programming principles, use a modern language like Java, C#, or Swift, or one of the dozens of others like Rust. Also, study the different kinds of programming paradigms, including functional.

Erik Eidt
  • 33,282
  • 5
  • 57
  • 91
  • 12
    C is just fine at creating abstractions. You can write methods in C, and you can package up data as structures in C. Entire operating systems have been built using C. What C *isn't* good at is satisfying everyone's "modern" idea of what object-orientation is *supposed* to look like. – Robert Harvey Aug 18 '16 at 00:27
  • @Robert, one could say the same about assembly language. – Erik Eidt Aug 18 '16 at 00:34
  • 4
    Not quite. C is quite a bit higher level than ASM. Just don't expect to use classes in it, though [there are ways to do that](https://www.cs.rit.edu/~ats/books/ooc.pdf), if you're so inclined. – Robert Harvey Aug 18 '16 at 00:38
  • @Robert, (1) sure we can: assembler is great at abstractions, as any good assembler supports supports structures and entire operating systems have been built using assembler... (2) and these "modern" languages are quite a bit higher level than C, what with namespaces, type safety, classes, etc.. and (3) yes, we can do OOP in assembler with arrays of function pointers, if we want. – Erik Eidt Aug 18 '16 at 00:50
  • 4
    Substitute "Brainfuck" for "assembler" in your comment, and it should still hold true. But that doesn't mean I'm going to be using Brainfuck any time soon. – Robert Harvey Aug 18 '16 at 00:51
  • 1
    @Robert, yes, that's my point. I'm just making it about C, while you're making it about assembler. – Erik Eidt Aug 18 '16 at 00:52
  • 6
    Your argument about C's features really only has merit if you believe classes (and garbage collection, etc.) are a necessary element of teaching a new programmer (which I don't). Nobody here is advocating trying to use classes in a classless language. – Robert Harvey Aug 18 '16 at 00:55
  • 5
    @Robert, my argument is that C is a lower level language, and as such it is hardly the best language for understanding principles of programming, which we can also say about assembler, and, just because they are lower level languages doesn't mean they are somehow closer to "true" principles of programming. These languages are better instead at understanding how hardware and operating systems works, and if that is the interest, I'm recommending skipping right to the lowest level, in learning instruction set architecture and machine language. Otherwise, there's lots of languages to choose from. – Erik Eidt Aug 18 '16 at 01:17
  • 1
    Compiler construction: runtime, calling conventions, object models, etc.. also would be of interest for trying to figure out how things work under the hood, and understanding the costs of various operations. But figuring out how things work under the hood isn't necessary for getting to the principles of programming; I think it can be a bit of a tangent. – Erik Eidt Aug 18 '16 at 01:20
  • 1
    You might have a point; even the author of [Learn C the Hard Way](http://c.learncodethehardway.org/book/introduction.html) recommends learning *Python* first. As much as I love C#, there are probably better languages for first timers. Java is a truly horrid choice. Swift and Rust might be better. – Robert Harvey Aug 18 '16 at 01:38
  • **asm** doesn't have structures. or arrays of structures. C is a much smaller and cleaner language than C++. i wish C did a few things different (like had `complex` as a primitive type and bumped up the precedence of `<<` and `>>` to above arithmetic), but i like the idea that I/O (like `printf()`) is **not** part of the language (but is part of a library). i **hate** the construct in C++ using `<<` to mean output (instead of just calling `printf()`). and i **hate** that C++ obfuscates the passing by value or by reference, which is annoying when i read other people's code. – robert bristow-johnson Aug 18 '16 at 16:01
  • and the code to do simple, nested arithmetic in **asm** is much more complicated than writing a line of C code. – robert bristow-johnson Aug 18 '16 at 16:05
  • 2
    @robertbristow-johnson, You're using an impoverished asm. See some examples for an assembler that support structures: http://www.hep.wisc.edu/~pinghc/asm5.html. Arrays are simply done with pointers. And yes, you are correct, assembler is lower level than C. – Erik Eidt Aug 18 '16 at 16:06
  • asm comes with whatever chip that i am working on. don't have that much choice. most asms have a macro expansion operator which you can use for some things, but that's still not as compact as accessing elements in a `struct`. BTW, i have been programming in asm for some chip or another for 4 decades. i certainly know how C pointers are addresses in asm. but an array of `struct` requires knowledge of the struct size and then offsetting that to get individual fields. – robert bristow-johnson Aug 18 '16 at 16:33
  • 1
    C is great at teaching some basic concepts, but some of its syntax is needlessly unforgiving, which often causes people to erroneously think that low level programming and hard, cryptic syntax somehow go hand in hand. From that point of view, I would gladly teach someone simple structs, algorithms and procedural programming using (say) C# and then move to C. – Theodoros Chatzigiannakis Aug 20 '16 at 12:39
  • @TheodorosChatzigiannakis, why is C# better than C for learning about `struct`s, procedural programming (is that about program flow, like using `if`, `while`, `for`, and the deprecated `goto`?), let alone **algorithms**? – robert bristow-johnson Aug 20 '16 at 20:29
  • 2
    @robertbristow-johnson There's no such thing as better or worse, but I know *I would prefer* to teach someone these concepts in C# instead of C. That's because then I could focus on the concepts themselves, without having to answer things like which top-level declarations need a semicolon or whether the asterisk is part of the type or the expression or why we need forward declarations or why we need header files or having the student entertain even momentarily the delusion that a program can read uninitialized memory and still be valid. – Theodoros Chatzigiannakis Aug 20 '16 at 22:20
  • @robertbristow-johnson What makes you think goto is *deprecated* in C#? Deprecated means it may be removed from the language at some point. – Brandin Aug 26 '16 at 09:59
  • @Brandin, i am using the term *"deprecated"* in the root or most most fundamental sense. [says Wikipedia](https://en.wikipedia.org/wiki/Deprecation): "**Deprecation** is the discouragement of use of some feature, design or practice, typically because it has been superseded or is no longer considered safe, without (at least for the time being) removing it from the system of which it is a part ..." – robert bristow-johnson Aug 26 '16 at 16:48
  • @robertbristow-johnson Well, you have every right to discourage its use, but it ain't going away any time soon. – Brandin Aug 26 '16 at 17:55
  • @ain't sayin' it's going away. i am saying it's "deprecated". `goto` is, essentially, superseded by `if{}else{}`, `while`, and `for`. (`for` is really a construction from `while`.) the only time i ever used `goto` was to construct, in a C program, a sorta pseudo-asm to demonstrate to an FPGA person how a particular integer division algorithm worked. i used `goto` because that is how the low-level code would look. otherwise no decent C code should have `goto` in it. – robert bristow-johnson Aug 26 '16 at 19:26
9

There are no principles, in the general abstract sense of computer science, that are present in C that are not also present in higher-level languages. All of computer science boils down to algorithms, and all algorithms can be implemented in any language that is Turing-complete as C is.

The difference that C carries apart form higher-level languages is similar to the difference that machine code carries apart from C: The relationship of the machine to the code.

As you write code in high-level languages, you do not (generally) concern yourself with how your code interacts with the machine. The virtual machine that the language defines for itself hides many of those aspects of code execution.

In C, your program's interaction with memory is held at the forefront. It is more than merely that you have to manage your use of the heap, it includes your code's interaction with the stack, and in how your code's mere access of memory affects the behavior and performance of your code - not even the order of memory accesses can be allowed to escape your attention, as reading the wrong memory at the wrong time can effectively cripple performance.

In higher-level languages, these things are simply not as obvious. Memory is allocated and deallocated without your knowledge, and sometimes without your prompting. Most often, this is simply out of your control. The when, where, how, and why of most memory allocations are simply hidden from you.

Likewise, in the other direction, writing machine code or assembly code brings yet more details to the foreground: Almost nothing is left outside of your purview, and your code must be written aware of every allocation, every resource, every piece of data that passes through the CPU's registers - knowledge that is so far removed from high-level languages as to be arcane.

greyfade
  • 11,103
  • 2
  • 40
  • 43
  • 6
    I've never quite understood why people always mention Turing-completeness in these kinds of discussions. Turing-completeness is not meaningful in this context; C is no different from other programming languages in that respect, so there's nothing to be learned about Turing-completeness by switching to C. The only time Turing-completeness becomes relevant is when discussing something that is *not* Turing-complete, like CSS or HTML. Hell, if you squint hard enough, [even those are Turing-complete](http://stackoverflow.com/a/5239256/102937). The more meaningful questions focus on *usability.* – Robert Harvey Aug 17 '16 at 22:04
  • 1
    In fact, if you squint hard enough, C is *not* Turing-complete: C guarantees that `sizeof` returns a finite size and that you can take the pointer of any object, and since I can ask the `sizeof` the pointer, and that is finite, there can only be finitely many objects of finite size. C is only Turing-complete when you combine it with a runtime system such as POSIX or Win32 that has APIs for auxiliary storage such as files. – Jörg W Mittag Aug 17 '16 at 22:14
  • 3
    @RobertHarvey My point is that so long as a language has a reasonable minimum of computational capability, its "level" becomes irrelevant from the point of view of algorithms, which are the only things that matter in this context. – greyfade Aug 17 '16 at 22:20
  • Language expressivity matters more, one you cross the Turing-complete threshold. Sure, you can write an algorithm in brainfuck, but why would you want to, except for masochistic reasons? – Robert Harvey Aug 17 '16 at 22:25
  • 1
    @RobertHarvey That's... entirely beside the point. I don't understand why you're harping on it. I'm not even remotely talking about that. I'm talking about how the level of a language is irrelevant except to the machine. Why are you talking about Brainfuck and CSS? – greyfade Aug 17 '16 at 22:52
  • 3
    Because you're making a point that is entirely inconsequential to the OP's question. Programmers do this all the time; they throw around the phrase "Turing-complete" like it actually has some meaningful use in practical terms. It doesn't, except to point out that *it doesn't matter.* – Robert Harvey Aug 17 '16 at 22:54
  • 2
    Biology is just a special case of chemistry, which is a special case of particle physics. There is nothing you can learn from biology if you know particle physics. Right? – Florian F Aug 18 '16 at 07:55
  • 3
    Worth pointing out that C simply requires you to make allocations and deallocations explicit, but they still concern an abstract machine, just like in "higher level" languages -- the physical (e.g.) x86 machine is still out of the scope of C. One can make a perfectly sandboxed VM that interprets C code but has no control over the memory allocations of the physical machine and it can still be a 100% conforming implementation. – Theodoros Chatzigiannakis Aug 20 '16 at 12:35
  • 1
    @TheodorosChatzigiannakis: Just to reinforce your argument: Emscripten is a C/C++ compiler that targets ECMAScript. It is complete and compliant enough to be able to compile stuff like MRI, YARV or CPython, which play pretty nasty tricks with pointers and memory (in their GC implementations), `setjmp`/`longjmp` (in their exception implementations), manually fiddling with the stack (in the case of MRI's green threads, for example), use of `goto` and even the GNU extensions computed `GOTO` (in CPython's bytecode interpreter loop), and it works well enough that you can just compile MRI to … – Jörg W Mittag Aug 22 '16 at 19:28
  • 1
    ECMAScript, pass it some Ruby code et voilà, you have Ruby running in the browser. In which case you will actually be running a garbage collector implemented in C using explicit memory management faked on-top of automatic memory management with a garbage collector implemented in C (maybe, depending on the browser) using explicit memory management. Then, there's the AS/400 which doesn't have pointers, files, threads or processes, which are basically the 4 primary abstractions in C (or C+POSIX, rather), where C is pretty much the *opposite* of "close to the machine", rather it *has to* run … – Jörg W Mittag Aug 22 '16 at 19:32
  • 1
    … emulated. (The modern successor to the AS/400 uses a POWER-based CPU which can be switched back into a normal addressing mode for C-compatibility, so that it's no longer *that* expensive and weird, but the original CPU was a 48-bit custom design that literally had not pointers. The only way to run C code was to run in a software emulator.) And an extreme example would be JPC, an interpreter for x86 native binary machine code written in Java for the JVM platform. Here, not even native binary machine code is "close to the metal"! – Jörg W Mittag Aug 22 '16 at 19:35
8

C and the (Abstract) Machine

Most programming languages are described in terms of abstract machines. Then, they are implemented using sets of tools like compilers, linkers, assemblers, interpreters, static analyzers, intermediate languages, and hardware that will collectively produce a result that honors at least all the expected behavior of the abstract machine, as observed by a program.

C is not an exception to the above rule. It is described in terms of an abstract machine which has no notion of your actual hardware.

As such, when people say that C teaches you how your computer actually works, what they usually mean is that C teaches you how C works. But C being so pervasive in systems programming, it's understandable that a lot of people start confusing it with the computer itself. And I would personally go as far as to say that knowing how C works is often more important than knowing how the computer itself works.

But still, C and the computer are different things. Actual hardware is actually complicated -- in ways that make the C spec read like a children's book. If you're interested in how your hardware works, you can always look up a manual and start writing code in an assembler. Or you can always start learning about digital circuits so that you can design some hardware on your own. (At the very least, you will appreciate how high-level C is.)

How do you learn? And how do you learn?

Okay, actually learning about hardware involves things other than C. But can C teach anything else to programmers today?

I think it depends.

  • There are some who would say that you can better learn a concept by working in an environment that offers it and encourages it, often in multiple ways.
  • There are some who would say that you can better learn a concept by working in an environment that doesn't offer it and where you instead have to build it yourself.

Don't be too quick to choose one of these possibilities. I've been writing code for many years and I still have no idea which one is the correct answer, or whether the correct answer is just one of the two, or whether there is such a thing as a correct answer on this issue.

I'm slightly inclined to believe that you should probably eventually apply both options, loosely in the order I described them in. But I don't think this is really a technical matter, I think it's a mostly educational one. Every person seems to learn in significantly different ways.

Exclusively in C

If you answered the above question by at least involving the second option I proposed, then you already have a few answers under your belt: anything that can be learned in higher-level languages can be better learned by re-inventing it in C or at least expanded by adding C to the mix.

But, regardless of your answer, there are certainly a few things that you can learn almost exclusively from C (and perhaps a handful of other languages).

  • C is historically important. It is a milestone that you can look at and appreciate where we came from and maybe get a bit more context about where we're going. You can appreciate why certain limitations exist and you can appreciate that certain limitations have been lifted.

  • C can teach you to work in unsafe environments. In other words, it can train you to watch your back when the language (any language) can't or won't do it for you, for any reason. This can make you a better programmer even in safe environments because you'll produce fewer bugs on your own and because you will be able to turn off safety temporarily in order to squeeze some extra speed out of your otherwise safe program (e.g. use of pointers in C#), in those cases where safety comes with a runtime cost.

  • C can teach you that every object has storage requirements, a memory layout, the fact that memory can be accessed through a finite address space, and so on. While other languages don't need your attention on these matters, there are a few cases where some acquired intuition can help you make more informed decisions.

  • C can teach you about the details of linkage and object files and other intricacies through its build system. This can give you a useful hands-on understanding on how a natively compiled program often goes from source code to execution.

  • C can bend your mind to think in novel ways through the concept of undefined behavior. Undefined behavior is one of my favorite concepts in software development, because the study of its implications on non-classical compilers is a unique mental exercise that you can't quite get from other languages. However, you'll have to reject trial-and-error and start studying the language in a careful and deliberate way before you can fully appreciate this aspect.

  • But perhaps the most important realization that C can grant you, being a small language, is the idea that all programming boils down to data and operations. You might like to look at things as modular classes with hierarchies and interfaces with virtual dispatch, or elegant immutable values that are operated on using pure mathematical functions. And that's all fine -- but C will remind you that it's all just data + operations. It's a useful mindset because it allows you to bring down quite a few mental barriers.

5

The reason why C is good for learning is not that it teaches any principles. It teaches you, how things work.

C can be compared with one of those good old cars from the 70s or 80s, which were just built to drive. You can tear them apart, screw by screw, and understand how each part works, and how it works together with the other parts that you can take in your hands to look at. Once you understand all the parts, you have a very clear picture how the whole operates.

Modern languages are more like a modern car where the engine is basically a black box, way too complex to be understood by the average car owner. Those cars can do a lot, heck, in these days the are actively learning to drive by themselves. And with that complexity and comfort, the user interface has been moved much further away from what is actually going on in the engine.

When you learn programming in C, you come into contact with a lot of the screws and nuts that a computer is made of. This allows you to develop an understanding of the machine itself. For instance, it allows you to understand why it is not a good idea to build a long string like this:

java.lang.String result = "";
for(int i = 0; i < components.size; i++) {
     result = result + components[i];
};

(I hope, this is correct Java, I haven't used it in a while...) From this code example, it is not obvious why the loop has quadratic complexity. But that is the case, and it is the reason why this code will come to a grinding halt when you have a few millions of small components to concatenate. The experienced C programmer knows instantly where the problem is, and will likely avoid writing such code in the first place.

  • I agree completely. C will teach you much about how things work, which goes a long way towards understanding a) why higher level languages were written (e.g., what they meant to abstract away), and b) how those higher level languages work behind the scenes, which for sure good knowledge to have. It won't teach principles and may not be as easy, but you'll come away with a respect for it, computers, and a deeper understanding of the higher level languages as a result. Also read Back to Basic by Joel Spolsky: http://www.joelonsoftware.com/articles/fog0000000319.html – jleach Aug 20 '16 at 11:01
  • 1
    Your answer is surely fine, but if you write `for(int i = 0; i < strlen(s); i++)` in C, the loop will also have quadratic complexity, and it is just as unobvious as in your Java example ;-) – Doc Brown Aug 20 '16 at 11:13
  • Not sure, but I would trust the Java compiler to optimiz this away :-) – Bruno Schäpper Aug 20 '16 at 11:41
  • The experienced Java programmer also knows instantly where the problem is, and will likely avoid writing such code in the first place. This specific issue is discussed in elementary Java tutorials. So this certainly isn't something that can be learnt from C but not from Java. – Peter Taylor Aug 23 '16 at 15:01
  • @PeterTaylor If you have a Java instructor who knows their C, yes. But I doubt that this sort of knowledge will stay around once the people who know C are dead. For the C programmer, it is the very definition of what a C-string is, which tells them that my code example cannot be efficient. For the Java programmer, it is some deep, arcane, and little used knowledge about the abstractions they use, which tells them to avoid this sort of code. Ok, I'm exaggerating a bit here, but you get the idea: The C programmer must know it, the Java programmer may get away with not knowing it. – cmaster - reinstate monica Aug 23 '16 at 16:46
  • When I say tutorials, I mean tutorials. E.g. the [official Sun tutorial](https://web.archive.org/web/20000620231218/http://java.sun.com/docs/books/tutorial/java/data/whytwo.html) (from back when Sun still existed). For the Java programmer, the "*deep arcane knowledge*" is that `String` is immutable. If you port the same example to other languages (I think PHP, for example) it's perfectly efficient, because they use P strings. So you almost seem to be making an argument that C programmers will learn to overgeneralise... ;) – Peter Taylor Aug 24 '16 at 07:12
  • Except that you wouldn't write that in Java, instead you would write something like `String result = Arrays.stream(components).collect(Collectors.joining())`. And in an even higher level language, you would write `components.join` (Ruby), `components.mkString` (Scala) or something like that. Yes, it's true, as a Ruby programmer, I pretty much forgot how to write `for` loops … because I don't need them, I have much better abstractions at my disposal. (In fact, Ruby doesn't even *have* a `for` loop.) – Jörg W Mittag Aug 25 '16 at 20:32
2

There are better languages than C to learn "the principles behind programming", especially theoretical principles, but C may be good to learn some practical, important things about the craftwork. greyfade's answer is surelely correct, but IMHO there is more you can learn from C than how to manage memory by yourself. For example,

  • how to create programs with a complete error handling in the absence of exceptions

  • how to create structure in a program without having any language support for object orientation

  • how to deal with data in the absence of data structures like dynamic sizeable lists, dictionaries or a useful string abstraction

  • how to avoid common errors like array overflows even when the compiler or run time environment does not warn you automatically

  • how to create generic solutions without having language support for templates or generics

  • and did I mention you can learn how to manage memory by yourself, of course? ;-)

Moreover, by learning C, you will learn where the syntactical commonalities of C++, Java, C#, Objective C come from.

In 2005, Joel Spolsky wrote a recommendation for learning C before any other higher level language. His arguments are

  • "you'll never be able to create efficient code in higher level languages."

  • "You'll never be able to work on compilers and operating systems, which are some of the best programming jobs around."

  • "You'll never be trusted to create architectures for large scale projects"

  • "if you can't explain why while(*s++ = *t++); copies a string, or if that isn't the most natural thing in the world to you, well, you're programming based on superstition

Of course, what he wrote is surely debateable, but IMHO lots of his arguments are still valid today.

Doc Brown
  • 199,015
  • 33
  • 367
  • 565
  • 1
    C is a very good language to learn before you learn those "higher level things that you think C doesn't support", because it helps you understand how those things actually work (and how expensive they are). – Brendan Aug 20 '16 at 11:02
  • @Brendan: I am not sure if the intent of your comment is to express an agreement, a disagreement, or just a sidenote to my answer. – Doc Brown Aug 20 '16 at 11:09
  • C programs written by professionals are released with buffer overruns and hard crashes. So, while a disciplined programmer *may choose to learn these things*, the C the language doesn't *inherently teach* merely by being impoverished. There are lots of bad ways (and worse, mixing multiple inconsistent ways) to do error handling without exceptions and create structure without objects, for example. – Erik Eidt Aug 20 '16 at 16:09
  • @ErikEidt: indeed, but especially in the C ecosphere there exist a lot of books, tutorials and other material which will deal with these topics. For example, "Writing Solid Code" by Steve Maguire.Of course, if someone just picks the first edition of K&R for learning the language syntax, he probably won't become a better programmer. – Doc Brown Aug 20 '16 at 16:53
1

The two basic abstractions of computing are Turing machines and Lambda calculus, and C is a way to experiment with the Turing machine view of computation: mostly a succession of low-level actions from which emerges a desirable result. But you have to keep in mind that C comes with its own model of computation. So, learning C will teach you the low-level details of the C abstract machine, which is getting quite different from actual architectures. One of the first thing I was taught in C was to never try to outsmart the compiler by applying "clever" tricks, and it seems that the trend is towards more and more optimizations in compilers. Like in other high-level languages, when you write in C, compilers sort of understand what you expect to be done on the abstract C machine and make it happen on the actual hardware, sometimes in very unexpected ways (reordering statements, etc.).

So what I mean is that learning C is not necessarily going to give you a good picture of what actually happens in the hardware. "C is close to the machine" should be understood as "closer than most higher-level languages". Learning software architecture directly is going to be more rewarding if you want a more accurate picture of "how it works".

On the other hand, learning C can familiarize you with system programming, low-level types, pointers and in particular memory allocation. As for learning algorithms and data structure, I don't there is an advantage to learn them in C rather than in other languages.

coredump
  • 5,895
  • 1
  • 21
  • 28
-3

It is very open ended question, may not yeild conclusive answer. You can learn pretty much everything in every language provided you understand the internals of the language framework. C forces you to understand lower details because it was primarily designed to write an operating system. Even after so many years, it is still one of most used languages primarily thanks to embedded systems and open source projects. So, the question is what you want to learn ? And in which domain?