News for hackers. Stuff that matters.
Re:Let's Destroy C by dilippkumar on Thursday January 30, 2020 @11:47AM (#22192801)
I've been a C programmer for several years now (out of necessity - C is still the best language for firmware and embedded development)

There's been a lot of noise on the internet about Rust and C++20 and "modern" languages. I recently had an opportunity to try some of these first hand.

Honestly the new language features are all terrible with few exceptions. In general, anything that was added to a language to support generics or to hide pointers and memory management from the developers on a language that isn't a lisp has only produced more harm than good.

I grew up as a programmer always thinking about the underlying CPU and the underlying memory and how my code would interact with both of them. It requires greater care as a programmer and tools like static analysers, code reviews and rigorous testing are extremely important. Improving these tools and coming up with new ones is far more useful (in my opinion) than trying to update C.

Yes C has it's flaws. Sometimes, those flaws are important and a replacement language is useful. I find that Go is a very interesting replacement for C at sufficiently higher up the stack. Something like Haskell probably will best fill in the gaps between C and Go.

I'm not convinced that fixing C with something like C++ was a good idea. Similarly, anything that's trying to fix C++'s problems is unlikely to come up with a decent way to have generics and hide memory and pointers from developers.

If I was going to create my own language, I'd keep it like C, remove some of the undefined behaviors, add support for Posits, 128 bit and arbitrarily wide signed and unsigned integers, features for explicit cache management (!!!), container classes as part of a standard library, Go-lang style interfaces and bake in something like cppcheck into the compiler.

/rant

Re:Let's Destroy C by wolrah on Thursday January 30, 2020 @12:41PM (#22193382)
> I grew up as a programmer always thinking about the underlying CPU and the underlying memory and how my code would interact with both of them. It requires greater care as a programmer and tools like static analysers, code reviews and rigorous testing are extremely important.

This is the double-edged sword of C and other lower level languages though. The programmer is given great power over their environment, but as the saying goes with great power comes great responsibility. Not all programmers are capable of handling that responsibility, and even those who are make mistakes from time to time. If you don't need the power, there's a strong case to be made for giving some of it up in exchange for also reducing the number of ways you could misuse it.

There will always be a place for low level programming in performance-critical code or extremely resource constrained environments, but there's a lot of software out there that spends most of its time waiting on I/O or user interaction while running on systems with gigabytes of free RAM and a half dozen idle cores. In those cases I believe the use of safer languages should be encouraged.

Re:Let's Destroy C by chillingeffect on Thursday January 30, 2020 @01:33PM (#22193892)
I understand it's against the rules to suggest someone hasn't read the article.

But I think it's pretty clear from this comment that the article has zero to do with the posted article. The article's title was provocative and humorous, bc it's abt using C in a very peculiar way. It's not about the tired argument of "should we replace C with Rust/etc." which the above poster and many others have glommed onto. This waterfall of irrelevant comments has no place next to this article which is abt a very specific, interesting set of techniques.

Re:Let's Destroy C by lhweroiuwer on Thursday January 30, 2020 @12:41PM (#22193377)
> remove some of the undefined behaviors,

There was a great talk from a C/C++ compiler writer about why you can't remove undefined behaviors while at the same time keeping C like speed.

Simple example: accessing an array past it's end it's undefined behaviour. If you want to remove this, you need to add bounds checking (either to raise an error or to just return 0).

Re:Let's Destroy C by audunw on Thursday January 30, 2020 @03:16PM (#22195055)
> There was a great talk from a C/C++ compiler writer about why you can't remove undefined behaviors while at the same time keeping C like speed.

I don't think this is true. Look at Zig, they don't seem to have a problem removing a lot of C's undefined behaviour, while still being able to surpass C in speed in many cases.

> Simple example: accessing an array past it's end it's undefined behaviour. If you want to remove this, you need to add bounds checking

Zig does indeed do bounds checking. I think the way it can still compete with C is: - Zig should be better at propagating constants (Better module system, Link-Time Optimization by default, and I think avoiding undefined behaviour helps here too). Arrays/slices do often have constant bounds. - You can choose to build with or without bounds checks (--release-safe, --release-fast). This means you're more likely to discover out of bounds problems during debugging, since you'll always get errors in those cases. But you have the option to release a fast version.

Julia has another interesting solution to bounds checking, where you can mark a piece of code with @inbounds to declare that you assume array access is within bounds.

I think some undefined behaviour can also be detrimental to performance. If you pass two pointers to a function, and it's undefined whether they alias or not, there are optimisations you can't do.

Re:Let's Destroy C by slrz on Thursday January 30, 2020 @08:06PM (#22197938)
> I think some undefined behaviour can also be detrimental to performance. If you pass two pointers to a function, and it's undefined whether they alias or not, there are optimisations you can't do.

I think this results from a misunderstanding of how undefined behaviour works in C. When a program exhibits undefined behaviour it is not a valid C program. The compiler may just assume (instead of having to prove) that it doesn't happen.

Example: the memcpy(3) standard library function. C says the behaviour is undefined if the given areas overlap. That means the implementation can perform optimizations "knowing" that there is no overlap. A valid C program can't possible invoke memcpy with buffers aliasing each other (because then, the program would be invalid). The compiler is not required to issue a diagnostic about these kinds of incorrect programs and just compiles your code assuming they don't exist.

Re:Let's Destroy C by T-hawk on Thursday January 30, 2020 @12:55PM (#22193499)
> you can't remove undefined behaviors while at the same time keeping C like speed.

This is absolutely correct. The specs of C allow so much undefined behavior in order to let the compiler just emit the instructions for the arithmetic operation or memory access or whatever. For edge cases like overflow and bounds, C deliberately says "not my problem" and you just get whatever that hardware architecture happens to do with that instruction. It's a deliberately leaky abstraction.

Re:Let's Destroy C by juped on Thursday January 30, 2020 @01:05PM (#22193608)
It's more that things were left undefined for portability and compiler authors (ab)used them for optimization enough for that to become de facto true (especially now that portability is easier).
Re:Let's Destroy C by favorited on Thursday January 30, 2020 @02:55PM (#22194825)
And if all C users decided that, now that portability is much easier, it was time to define some of that behavior, it would happen.

But, as much as people want to blame optimizing compilers for their behavior around UB, absolutely no one is going to sacrifice C's performance for safety.

Re:Let's Destroy C by tylerhou on Thursday January 30, 2020 @09:52PM (#22198558)
Portability is still nontrivial. It’s still difficult to target all of x86, ARM, Power, RISC-V at once.
Re:Let's Destroy C by tynorf on Thursday January 30, 2020 @12:49PM (#22193446)
I haven't actually watched it in a while, but are you referencing this talk by Chandler Carruth? https://youtu.be/yG1OZ69H_-o
Re:Let's Destroy C by dooglius on Thursday January 30, 2020 @01:20PM (#22193742)
Only if you define it to be something dumb like an error or returning 0. It should instead be defined as the hardware behavior: a load to the resulting linearly-calculated address which enters an implementation-defined exceptional state (e.g. SIGSEGV/SIGBUS) if that address does not represent readable memory.
Re:Let's Destroy C by Dylan16807 on Friday January 31, 2020 @05:35AM (#22200594)
There are a lot of undefined behaviors that only exist out of inertia and laziness. For example there's a bunch of things that should be syntax errors that are instead UB.

> Simple example: accessing an array past it's end it's undefined behaviour. If you want to remove this, you need to add bounds checking (either to raise an error or to just return 0).

What's wrong with saying "it will either return an unspecified value or trap"?

Re:Let's Destroy C by jgwil2 on Thursday January 30, 2020 @01:11PM (#22193661)
> In general, anything that was added to a language to support generics or to hide pointers and memory management from the developers on a language that isn't a lisp has only produced more harm than good.

I understand why you would want explicit memory management, but what specifically is harmful about generics?

Re:Let's Destroy C by nimrody on Thursday January 30, 2020 @01:09PM (#22193642)
Have you looked into Zig (https://ziglang.org/)? It's a little more than what you've asked since it does support compile time generics. But is specifically targeted at removing undefined behavior and improving safety.
Re:Let's Destroy C by audunw on Thursday January 30, 2020 @03:01PM (#22194888)
I can second this recommendation. Zig is really the best language I seen so far at attempting to be a direct replacement to C (Rust is good too, but maybe a bit too advanced.. feels more like a C++ replacement)

The compile time generics is kind of necessary to replace some of the stuff people use C preprocessor macros for. I think Zig is just taking the idea of having compile time evaluation of code to its natural conclusion, and it ends up being a lot cleaner than C with preprocessor magic.

The only thing they've added which isn't necessary for a "better C", is the async stuff they're working on now. But I think it's still a good idea.

Re:Let's Destroy C by AnimalMuppet on Thursday January 30, 2020 @12:45PM (#22193418)
For embedded, C++ can still be useful. You just won't use most of it. I still like using classes; destructors can free up more resources than just memory. But yes, you can get most of what you need out of C.

I also suspect that Haskell fits above Go, not between Go and C.

Other than that, I'm pretty much in agreement with you.

Re:Let's Destroy C by SuspiciousSwan on Thursday January 30, 2020 @12:21PM (#22193176)
Even a subset of that would be amazing to me -- "removed all of the undefined behaviors, added 128 bit signed and unsigned integers, features for explicit cache management (!!!), and container classes as part of a standard library"
Re:Let's Destroy C by UncleMeat on Thursday January 30, 2020 @07:34PM (#22197683)
> I grew up as a programmer always thinking about the underlying CPU and the underlying memory and how my code would interact with both of them. It requires greater care as a programmer and tools like static analysers, code reviews and rigorous testing are extremely important.

And yet, even projects filled with extremely strong engineers who participate in all the best practices and run interprocedural static analysis and sophisticated fuzzers still write piles of security vulns in c programs.

Re:Let's Destroy C by Upvoter33 on Thursday January 30, 2020 @01:41PM (#22194000)
Great post.

In particular: "remove some of the undefined behaviors" I think the confusion caused by this is not nearly worth the optimization gained.

Compiler folks will say "but look, this loop is 58% faster!" but ignore the fact that slow code can be optimized through other means, including profiling, restructuring, etc.

Re:Let's Destroy C by beached_whale on Thursday January 30, 2020 @01:48PM (#22194085)
Or avoid them when your compiler hasn't documented if they support it. Such as gcc supporting union type punning in c++. Many compilers allow for reinterpret_cast'ing too, e.g. gcc and -fno-strict-aliasing

This is more of knowing your language and tools. UB isn't some magic beast either, it's where it isn't really feasible for a language that runs on many platforms to dictate what happens. What should the std's say when you shift a signed int too far? Often the HW will have a way of doing it and others will not, so either don't do that or know your implementation

Re:Let's Destroy C by xtian on Thursday January 30, 2020 @12:41PM (#22193380)
Have a look at Zig and Odin.
Re:Let's Destroy C by metalliqaz on Thursday January 30, 2020 @11:57AM (#22192903)
I feel the same way you do, but I still want to spend more time getting to know Rust.
Re:Let's Destroy C by mrlala on Thursday January 30, 2020 @12:42PM (#22193391)
>In general, anything that was added to a language to support generics or to hide pointers and memory management from the developers on a language that isn't a lisp has only produced more harm than good.

Why should I care or need to worry about pointers and memory management in every part of my code?

Yeah as a C# developer I need to be aware if I'm passing a variable by value or reference.. but I don't want to and should not need to define this all the time. I know that built-in simple types (int, string, double) etc are passed as value... and everything else is passed as reference. So it's basically a non-issue.

I grew up learning C/C++ and having to deal with all the crap. Why oh why do I want to handle all of this myself?

I just want to code and focus on getting things done... C#, for example, allows me to do that.

You saying these things have "produced more harm than good" is so obviously coming from a more academic standpoint, or purist in the sense of "oh my god he doesn't even realize that those 8 bytes are going to be held up until the garbage collector comes, whereas I can deallocate that precious memory right away!". Sorry but almost no one cares. Yeah there are times where you need to care, but for most developers that time is... never.

The elitist attitudes on HN are astounding sometimes. Heaven forbid someone code without also managing every aspect of the underlying hardware!

Re:Let's Destroy C by 4D1 on Thursday January 30, 2020 @12:55PM (#22193501)
How do you think these luxuries you were provided were given to you?

If you are operating within a managed usermode level of an operating system then what you say is perfectly valid, especially for programs that do not require high availability such as web servers. You can program in a managed environment and trust in your languages JIT/GC to handle low level optimization.

On embedded systems, software that requires high availability (video games for example), or kernel mode drivers and firmware, you are not always allowed that luxury. Understanding of and strong micromanagement of how memory is allocated and moved around the system becomes more and more crucial. It could be hardware limitations of the device that cause this, or in the case of firmware or drivers, any overhead that would be acceptable in a usermode application will be felt throughout the environment when you work on low-level.

TLDR you and the parent post are talking about apples and oranges.