It is of course useful for a whole bunch of reasons, including being able to figure out "why does this do X?", but that's not really "deeper knowledge", IMO.
I think C lets you think about low-level details of how systems work, strings being arrays of characters with an explicit terminating NUL character, for example. Although again assembly language gives you _real_ lower-level understanding and insights if you use it.
I "just" want to do X, but instead I need to write a whole bunch of code to handle it all manually. It's useful that this is possible, but a lot of times you don't need it, and some other languages still keep the "common case" easy while still enabling the high-performance stuff C can do.
I wish we had C+: C + a few niceties (and not C ++ everything). There's a whole bunch of newer languages aiming at the space C is sitting in, but with a few additions C could be much more ergonomic without having to invent an entire new language.
I’ve made a pre-processor for C to add some things I miss, although it is currently limited to what can be done without type information and has to keep compatibility with existing C syntax: https://sentido-labs.com/en/library/cedro/202106171400/
There is another language called C3 that “is a C-like language striving to be an evolution of C, rather than a completely new language”: https://github.com/c3lang/c3c
If you have the time, I’d like to hear which things you miss in C. There might be something I did not imagine that could be added to Cedro.
C is a something of a high-level assembly language. Of course we could write literal assembly language in C, pretty much.
r1 = 0
r2 = 1
r3 = r1 + r2
mem[1000] = r3All the low-level languages aiming to replace C basically abandoned this approach, and opted for slices instead (ptr + size pair, used in Rust, Zig, etc.) This has the benefit of easier and faster string manipulation (strlen can be O(1) instead of O(n)), as well as better memory safety (can do bounds checking with the size information). The primary reason we are still using and teaching null terminators is because we still have to live with POSIX/Win32 and many low-level libraries written on this platform.
String literals are constant single-item Pointers to null-terminated byte arrays. The type of string literals encodes both the length, and the fact that they are null-terminated, and thus they can be coerced to both Slices and Null-Terminated Pointers
https://ziglang.org/documentation/master/#String-Literals-an...
Zig's string literals (which you create with parenthesis like "Hello world!") are null-terminated byte arrays, expressed as the type *const [N:0]u8 (where the :0 tells you that it's null-terminated), whereas the more typical array might be written as *const [N]u8. The reason for this feature is not because the language wants you to use null-terminated strings, but because these static strings need to be stored in the global data section of the ELF executable, and these require you to use null-termination. But if you want to do any mutable operation with this string, you need to convert this into a proper slice (ptr + size). And it seems like Zig developers don't really use null-terminated types that much at the API level, but use it for things like C interop or cases where you really need it for special optimizations.
Noting that from the PR that introduced this feature, Andrew Kelley writes [0]:
> I think you will find that the Zig community in general (and especially myself) agrees with you on this [null-terminated strings being fragile], and APIs in general should prefer slices to null terminated pointers. Even if you are using Zig to create a C library, and even in actual C libraries, I would recommend pointer and length arguments rather than null terminated pointers, like this: https://github.com/andrewrk/libsoundio/blob/1.1.0/soundio/so...
> That being said, I want to repeat what I said earlier about null terminated pointers: A null terminated array is not inherently an evil C concept that is intruding in the Zig language. It's a general data storage technique that is valid for some memory constrained use cases. I also stumbled on a Real Actual Use Case inside LLVM. The bottom line for me is that null terminated pointers exist in the real world, and especially in systems programming. You can see this in interfaces with the operating system in the standard library...
So he acknowledges null-terminated strings can certainly be useful in certain situations outside of legacy reasons, which is good to know. And Zig creating a special type for this shows that a good systems language needs to be designed to accommodate the needs of the outside world.
[0] https://github.com/ziglang/zig/issues/265#issuecomment-46337...
Small correction regarding the ELF object file format: any kind of data whatsoever can be stored in the data section, including strings in the form of ptr + length without null termination. Zig string literals are null terminated in addition to having the length not because of an object file limitation but because it is useful to pass string literals to APIs that require null-termination, such as most C libraries. In practice, this simplifies the experience of using the language.
I think you can eck out more performance, but the time investment is much more.
It is always "worth it" to understand the machine you are manipulating, and no matter what you think about the C virtuel machine, the very real machine in front of me has been shaped by its relationship to C.
> It is always "worth it" to understand the machine you are manipulating
Why is it always worth it? If I want to be a product/creative developer how does learning C specifically help me?
It's like knowing how your car works will make you a better driver. You'll be able to get more out of the car, it will last longer, and when it breaks you'll know if you can't move another inch or you'll know how to nurse it home.
I'm not sure what you mean by this. I know what pointers are and I have not written C before. What understanding will C specifically give me which will help me understand reference types better?
> It'll also give you a good feel for what kinds of operations are costly and what kinds are not
Does this matter though? For most applications you don't need to understand what kind of operations are costly.
I am unusually curious so I'll probably learn about Assembly and low level operations out of curiosity at some point, but it's easy to imagine that I may not work on something where this would be useful knowledge or I would use this knowledge so rarely that the opportunity cost is not worth it.
For the vast majority of developers the opportunity cost for learning C seems quite high.
Actually working with them confers a lot more understanding of them.
> For most applications you don't need to understand what kind of operations are costly.
I've often been given the task to fix severe performance problems in other peoples' code who simply did not know what kinds of operations were costly. Having performant code is a significant competitive advantage.
> For the vast majority of developers the opportunity cost for learning C seems quite high.
Not if you understand pointers. That's what usually trips up people new to C.
> it's easy to imagine that I may not work on something where this would be useful knowledge
This may be hard to explain, but you don't know what you don't know. You won't be able to see an opportunity to exploit your knowledge gained from using C. To go back to the car analogy, most people drive a car with zero knowledge of how it works. This winds up being expensive for them, when it's time to deal with problems with the car.
For example, the alternator shorted out in my car. I took it to the dealer for a repair, and they quoted me a fantastic sum including 2 hours of labor. I happen to know how the alternator works, and I've replaced alternators multiple times. I gave the service manager the step-by-step procedure to replace the alternator, and said it could be done in 15 minutes tops. The labor price quote dropped by more than half. It also turned out that they could get me a refurbished one for far less money, and since I understand what a refurbished alternator was, I was comfortable with installing a refurbished one.
Anyone not knowing how cars work would have had no ability to bargain or understand the tradeoffs.
> I've often been given the task to fix severe performance problems in other peoples' code who simply did not know what kinds of operations were costly
What kind of application was this? I completely believe having this knowledge is valuable, but not for all developers.
> This may be hard to explain, but you don't know what you don't know. You won't be able to see an opportunity to exploit your knowledge gained from using C
This comes back to opportunity cost. You are stating this as though C is the only avenue to gain knowledge which can be exploited at a later date. For many developers there is probably other knowledge which is going to be far more useful and exploitable.
Your story about the alternator feels like a good example of this. If I was in your shoes I would have paid the original sum and been happy. You've saved money, but you've also had to learn how an alternator works and replace them multiple times. I'm happy to pay a premium and use that time for other things.
Take the plunge. Learn how to swim, how to touch-type, how compound interest works, how to read a balance sheet, how to tie various knots, how to drive a stick shift, how to integrate and differentiate, how to unclog a drain, how to jump start a car, how to change a tire, how to write a program in C.
As an old guy, I know these simple skills pay off in various and unexpected ways.
The arguments against learning fundamentals exist, but are weaker than the arguments for.
I am going answer you with some questions!
What is a product for you?
Are a dishwater, a car, a keyboard, a smart LED lightbulb some kind of product a human can build? A product with some sort of microcontroller?
How many products like that there are in your room, house and office right now?
What about in the whole world for the past 20 years?
In all of those products there is probably C running!
This is what I mean by a product developer: https://blog.pragmaticengineer.com/the-product-minded-engine...
I'm sure that type of developer exists for the kinds of products you are talking about, deep tech products, etc but that seems a lot less common.
The link you've posted also applies to those who build smart lightbulbs.
> seems a lot less common
I guess the problem is that the world is bigger than you may think. I don't know what you build, but let's pretend it's some kind of web service.
Consider how many brands of microcontroller-based products there are in the world, with their many variants. For the sake of the discussion consider just the "commercial" products you can purchase on Amazon. My guess is that they surpass the quantity of web projects out there.
Add to the mix the custom/industrial devices, special tools and computers, etc. that many small companies are building right now (like the one I work for).
Remember that someone is writing code for these products. Given the quantity of product, they can't be "a lot less common".
To answer your question, and if you do web services, let's say you want to know how Redis works internally. You need C.
Even in the web world, most devs are not going to be poking around in Redis internals. If you go higher up the stack to things like accessibility or CSS animations then knowing C is literally useless.
Why do you feel like C is so fundamental that every developer must know it regardless of what they work on?
I never said so. I was answering specifically to "If I want to be a product/creative developer how does learning C specifically help me?"
Since you didn't mention what you do, I was talking about "product" and the definition you have, and if you want to build "product" there is a whole "product" industry, that is bigger than you think, that requires C.
If you do web development you can completely skip it if you want, but C is a nice tool to have around to understand why there is a bottleneck in Redis, Sqlite, a driver or a stack. Maybe find a workaround or a fix, but waiting for someone else to fix it is also valid if you don't want to fiddle with C.
It happens all the time: high level developer points finger and says "there is something wrong with the filesystem, my app crashes". They cross their arms and wait for me to go in a crazy debug session so I can explain them later why their app is slow and/or crashes.
I'm well aware there are a lot of places C is used. My point is mostly that "waiting for someone else to fix it ... if you don't want to fiddle with C" is probably a better strategy than becoming competent enough with C to fix your own problems.
Becoming competent enough with C to diagnose and fix problems in production C codebases seems like it would be quite time consuming.
Also, I work with SREs that debug problems with MySQL and Redis without knowing C or looking at C code. Unless you are actively contributing to those codebases it seems unlikely you need to know C.
also c is fun!
I would argue that assembly language and hence C has been shaped that way it has because of the underlying structures of the processor and the way binary numbers are manipulated and stored. C and for that matter assembly, becomes quite trivial if you know enough to make your own simple CPU with decoder, instruction set, memory, ALU, FPU, etc.
Verilog is of course C-like (much as VHDL is Ada-like.)
If you care about execution time, responsiveness, efficiency, or reliability, these things matter.
If you're doing systems programming, they are essential.
Do you learn how to fly when you book a flight? Do you learn how to build cars when you get your driving license? Do you learn how water is cleaned when you drink? There is infinite regress.
In the end it is about trust and abstractions. Most algorithms have nothing to do with C. They are implemented in C.
C is a great language, but as someone said here, there is only so much time.
And how many people who can't even be bothered to learn C will dive even deeper into assembly?
And yes, I would think anyone who books flights/drives/drinks water for a living would be well served by learning something of the tools of their trade.
You don't need to know how to heel-toe to drive to the market, but pretending that it's not a useful skill to master driving is ridiculous.
Are you driving to the market, or are you a serious professional?
And it wasn't even proper C, it was RatC at the time.
There are plenty of alternatives to expand our brain in systems programming, and even better they show we don't need to compromise on security to do it.
However, yes one needs to learn C, because it has snuck everywhere and as the COBOL of systems programming languages (ruffly 10 years younger), it isn't going anywhere.
Lorem ipsum is typically a corrupted version of De finibus bonorum et malorum, a 1st-century BC text by the Roman statesman and philosopher Cicero, with words altered, added, and removed to make it nonsensical and improper Latin.
I don't think that really counts as "latin", any more than hackertyper[2] counts as C code.
[1] https://en.wikipedia.org/wiki/Lorem_ipsum [2] https://hackertyper.net/
Chick-pea must be rolling in his grave.
I guess we could continue this thread ad infinitum because there are plenty more examples but I'll leave this as de facto proof that Latin remains used: Et cetera.
To be fair, I've encountered Latin the most in philosophical texts which I can't say are read all that often