Live data from Hacker News

How can C Programs be so Reliable? (2008)

tratt.net

81–90 of 97 posts

Re: How can C Programs be so Reliable? (2008)

#81

Earlier quoted context omitted.

Then learn an assembly language instead, because C also has a fair amount of bookkeeping hidden behind the scene. C is typically described as a "low-level" programming language, where the "low-level" normally refers to the supposed distance from the language to the actual hardware. But as many incidents with UB demonstrate, this distance is still quite larger than expected. I think there is another sense of the word…

> Then learn an assembly language instead Learn assembly TOO, not instead. I did, as part of computer architecture course. Very valuable. I think you should learn everything from transistor level up if you want to do serious programming. I don't think you need to actually use it, but it's sometimes very handy to know those things.

It's important to set a correct expectation for what you learn. C is indeed important to learn because of all legacy and current code bases, but it's just one of possible language choices for learning computer architecture, and learning C alone doesn't give you a relevant knowledge.

Re: How can C Programs be so Reliable? (2008)

#82
post #31

Earlier quoted context omitted.

So it is worse, therefore it is better? Exceptions are exceptionally good at error handling - they always do the correct default (bubbling up if not handled, bringing a stacktrace with them, and by default they auto-unwrap the correct return value, not making the actual business logic hard to decipher), plus they make error handling possible on as wide scope as needed (try block vs a single return value). I absolutel…

> Converse curiously; don't cross-examine. Edit out swipes. > Please respond to the strongest plausible interpretation of what someone says, not a weaker one that's easier to criticize. > Eschew flamebait. Avoid generic tangents. In particular you seem to be responding to a strawman, since nowhere did I say C error handling was better than structured exceptions. The parent asked what was functionally different betwee…

You wrote:

> in C the program is more liable to simply crash

I simply disagree with this statement, as silent failure is also very common in case of C, which is probably the worst option. (Especially that it may cause memory safety issues, that may not even materialize until much later).

No need to take my comment that seriously though, I had no bad intention whatsoever.

Re: How can C Programs be so Reliable? (2008)

#83
post #74
post #36

I think the reliability gap is in statically typed, compiled languages versus dynamically typed languages. I think C++ is a good combination of both worlds. You don't have to type quite as much for error checking an manual management and you get a correctly typed program by default.

Static typing is useless without strict typing. Knowing the type of everything won’t save you if you can multiply a pointer by an integer and use the result as a file handle.

> Static typing is useless without strict typing. Knowing the type of everything won’t save you if you can multiply a pointer by an integer and use the result as a file handle.

What language are you talking about? Go to godbolt and try that with any of the compilers there for C or C++.

Re: How can C Programs be so Reliable? (2008)

#84

Earlier quoted context omitted.

> As the article says, checked exceptions are not the solution here. Java had a very bad model of checked exceptions (the OP was written in 2008). A correct way is to make it a part of the type system, though it doesn't have to be a sum type like Rust, and make any error-related code path as convenient as possible to use. > In C, you are forced to perform the NULL-check, or crash. You don't necessarily crash if you f…

> You don't necessarily crash if you failed to perform a NULL check! In the case of using the result from `fopen`, I don't know of a platform where a dereferencing of NULL (which happens in a separate translation unit, which is already compiled and linked, and will not be subject to LTO and other optimisations) within the various read/write/seek/tell functions doesn't result in an immediate crash. I fully admit that…

> I don't know of a platform where a dereferencing of NULL (which happens in a separate translation unit, which is already compiled and linked, and will not be subject to LTO and other optimisations) within the various read/write/seek/tell functions doesn't result in an immediate crash.

Although in a very different content, I have seen "dereferencing" a null pointer in C++ not crash immediately, if you dereference it to call a nonvirtual class member function, e.g,

    t->foo();
Depending on how this gets compiled and the implementation of `foo()`, the segfault may not come at the line above, where technically `t` is being dereferenced. It may come inside `foo`, or somewhere further down the call chain. The resulting crash may not even manifest as a segfault.

Re: How can C Programs be so Reliable? (2008)

#85
post #31

Earlier quoted context omitted.

> Writing code with the occasional try/catch block isn't too different from writing C and not checking error conditions I think a critical difference is that in C the program is more liable to simply crash if errors aren't correctly handled, whereas in Java/Python/etc the program can just log a stack trace and keep on truckin', even if the bug is actually quite severe. In some cases a crash is preferable - e.g. if so…

So it is worse, therefore it is better? Exceptions are exceptionally good at error handling - they always do the correct default (bubbling up if not handled, bringing a stacktrace with them, and by default they auto-unwrap the correct return value, not making the actual business logic hard to decipher), plus they make error handling possible on as wide scope as needed (try block vs a single return value). I absolutel…

> I’m sure the errno state is not checked at every line, as it is a trivial human error to leave that out

While writing code it's trivial to ask yourself if the next statement will include a call to a function which is not defined within a compilation unit under your control.

If it will, then you lookup documentation for that function to determine what it expects and how it can fail. Most of my functions which interact with such functions look like long sequences of this:

  /* close the file */
  ret = -1;

  do {
    errno = 0;
    ret = close(fildes);
  } while (0 != ret && EINTR == errno);

  if (0 != ret) {
    perror("Error");
    goto off_ramp;
  }
I've caused plenty of bugs in my career, but I can say with confidence that 0 of them had to do with ignoring/skipping proper error handling. You have to do so intentionally.

Re: How can C Programs be so Reliable? (2008)

#86
post #50

Earlier quoted context omitted.

> C is horrible for exploratory programming Completely disagree. The lack of screwing around selecting abstractions forces you to make something productive right away and not stress about refactor.

Who is stressing you to refactor when doing exploratory programming? Yourself? OOP languages? Society?

[deleted]

Re: How can C Programs be so Reliable? (2008)

#87

I think every graduating student should work on a non-trivial application in plain C for a year before moving on to another language. It makes you exceptionally paranoid about failure states and practically requires a bit of thought and planning before attempting any non-trivial change. The mindset of "it's fine to ignore all error conditions and let the default exception handler print a stack trace to the user" resu…

Rust is better for this since your program won’t even compile until you’ve handled those error states.

It will compile just fine if you call unwrap on a Result and then you get a wonderful error. If you call unwrap on a File::open() you do not even get the file name.

If you run this

use std::fs::File; pub fn main() { let fp = File::open("test").unwrap(); }

You get this:

thread 'main' panicked at /app/example.rs:11:33: called `Result::unwrap()` on an `Err` value: Os { code: 2, kind: NotFound, message: "No such file or directory" } note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

The language incentives you to propagate errors up to main and then just let it fail.

Re: How can C Programs be so Reliable? (2008)

#88
post #24

Earlier quoted context omitted.

Well, there are such tools for C, but wouldn't using them be detrimental in this context? Think, like using a debugger vs. trying to wrap the execution in one's mind: I'not saying that one shouldn't use debuggers, but not using one has benefits, as a teaching device. Like running in a weight vest. Edit: ah, perhaps you meant, in addition to using raw C, one should also learn how to use such static analyzers & cie

As the author notes, to know what C code does you need to run it. A good debgger is a C programmers best friend.

A debugger can only tell you what the executable does: the program compiled with a particular compiler on a particular platform, with particular code generation options.

It can tell you that i = i++ increased i by 2, for instance. That might not even be true of another instance of i = i++ in the same object file being debugged.

There is no substitute for knowing what the C will do before it is run.

Re: How can C Programs be so Reliable? (2008)

#89

Earlier quoted context omitted.

Rust is better for this since your program won’t even compile until you’ve handled those error states.

It will compile just fine if you call unwrap on a Result and then you get a wonderful error. If you call unwrap on a File::open() you do not even get the file name. If you run this use std::fs::File; pub fn main() { let fp = File::open("test").unwrap(); } You get this: thread 'main' panicked at /app/example.rs:11:33: called `Result::unwrap()` on an `Err` value: Os { code: 2, kind: NotFound, message: "No such file or…

You still have to explicitly do this and are made aware of this failure state. It's also much easier to reject PRs when you see an unwrap than to try to think of all the ways it could invisibly fail.

Re: How can C Programs be so Reliable? (2008)

#90
post #28

Earlier quoted context omitted.

Rust is better for this since your program won’t even compile until you’ve handled those error states.

Back when every developer had to build their own failure detection code and display messages to help themselves debug anything that meant that almost every developer was good at communicating errors to the user. Today developers don't build that skill, so you see applications that just fails silently everywhere or produce nonsense errors. The better developer tools you have the less your developers will need to learn…

Back in those days the error you got was "Segmentation fault (core dumped)"

Now its an exact line number with a little description of what went wrong and sometimes a suggestion on how to fix it.

Post reply on HN