11 ms·
The thing is, C also makes me aware of what is happening, by letting programs fail when I forget about certain things. This may not be as nice as the compiler
by usrbinbash 5y ago
The thing is, C also makes me aware of what is happening, by letting programs fail when I forget about certain things.
This may not be as nice as the compiler yelling at me even before the program is run, but it certainly makes for a much simpler language.
- eklavya 5y agoSure and sometimes it directly tells your users, bypassing you entirely :)
- kaba0 5y agoThe thing is, C won’t even fail in a reproducible way. Good luck hunting down mistakes caused by uninitialized variables for example. Sure, valgrind exists, but that is not native execution.
- usrbinbash 5y agoThe reproducible thing about this error would be: I forgot to initialize my variables.
- MereInterest 5y agoThat results in undefined behavior, which is by definition, not reproducible.
- usrbinbash 5y agoThe behavior of the program is not reproducible. The cause of the error is.
- programmer_dude 5y agoI think you are confused. The cause is not something you reproduce. It just exists. You can however "isolate/determine" the root cause.
- usrbinbash 5y agoIf I have 10 different errors, but the root cause for all of them is that I forgot to do something I should have done (initialize a variable), then the cause is the same across all 10 different errors. If I don't start initializing the variables, then I will continue to have erros (which may or may not be similar to each other), thus making the problem reproduceable. The errors are not the problem, but the result of the underlying problem.
- MereInterest 5y agoIf you have an error that only occurs on your computer, on your compiler version, on your particular optimization level, on this particular phase of the moon, how do you determine that the root cause is forgetting to initialize a variable? In the context of debugging, "reproducible" means "can reproduce that exact error in order to better identify the root cause", not "frequently occurs as a root cause".
- 10000truths 5y agoMemorySanitizer will catch uninitialized variable errors.
- tialaramex 5y agoThe Sanitizers catch errors that happen when they're running So in many situations the Sanitizer gives you a false sense of security that your code is correct because in the test environment, with the Sanitizer, it works, and then in Production, without a Sanitizer, under some circumstances it blows up because those circumstances never arose in your test environment. Compare (safe) Rust which doesn't have uninitialized variables, and even WUFFS which doesn't have array bounds errors. In both cases the entire problem doesn't exist, programs which exhibit this problem are not valid programs in those languages, which is great because we did not mean to write these programs anyway. Hooray.
- bregma 5y agoWhy would runtime checking for a condition that could be caught at build time be in any way a more desirable solution?
- kjeetgill 5y agoThat reminds me of my favorite bug in a C program I ever delt with. We had a dorm floors' worth of "Intro to C" students pouring over our friend's buggy program. With a enough printfs() I was able to whittle the problem down to: A local variable "b" was changing after some function call that looked like: "call(&a)". Turns out because our program looked like "{ int a[3]; int b; ... call(&a); }" an off by one error accessing "a[4]" in the function was overwriting variables never passed to it! Felt like the champ figuring out. (This is a 15 year old story from freshman year, so I hope I got the details right.)
- nindalf 5y agoThat’s horrifying.
- cozzyd 5y agovalgrind is your friend!
- kjeetgill 5y agoUpdate, since this got attention: I remembered slightly wrong: "b" would need to be declared before "a", not after and you'd access it with "a[3]" not "a[4]". So after this, "b" would be 7: "int b=5; int a[3]; a[3]=7;". Looks like I'm still making the same mistakes. But still: If you passed the array and referenced it wrong it would change local variables in some remote stack frame.
- AnyTimeTraveler 5y agoBut C doesn't let programs fail. (or at least not loudly) That's the problem, to me at least. I've had this code yesterday on an embedded arm cortex m0: bool tx_byte(char msg) { for (int i = 0; i < 8; i++) { tx_bit((msg >> i) & 1); } } This function wouldn't return. What's even stranger, the variable i would increment past 7. The for-loop just continues. I got a warning about the missing return statement in that function, but didn't think there could be a connection to my bug. It turned out that adding that return after the for loop fixed it. The warning was one among about 100 others, as I was refactoring quite some code at the time. I only started working on the warnings after I had decided that me an two of my colleagues were just overlooking something and would fix the bug another day. All that I wanted to say is that, no, C doesn't make you aware of what is happening. It has this giant pit of UB that does weird things when you fall into it. I was lucky that in my instance, I got a warning. I know other UB just works most of the time and goes unwarned.
- ascar 5y agoSounds like the compiler didn't correctly adhere to calling conventions without an explicit return statement. Wouldn't that even be a compiler bug allowing no return statements, but then incorrectly handling scope?
- tux3 5y agoIt's undefined behavior, the compiler can do whatever it wants. Here the compiler assumes the function never returns (because it would be UB since there's no return statement), so it looks like it just didn't bother to add a loop exit at all. This is standard compliant.
- nybble41 5y agoActually it's only undefined (per C99 anyway) if the return value is used: > If the } that terminates a function is reached, and the value of the function call is used by the caller, the behavior is undefined. So if the return value is never used then there is no undefined behavior and a standards-compliant compiler isn't allowed to optimize away the loop exit condition.
- catlifeonmars 5y agoI don’t think that a simpler language and a helpful compiler are mutually exclusive. In the article, the author even points out multiple examples where Go (a simpler language, relative to Rust) could output more helpful error messages. I assert it’s possible to have a simple language where it’s difficult to shoot yourself in the foot. C just happens to be a simple language where it’s exceedingly easy to shoot yourself in the foot.
- kaba0 5y ago> I assert it’s possible to have a simple language where it’s difficult to shoot yourself in the foot Zig would be one such example.
- sgt 5y agoSo why is Zig being dwarfed by Rust in this arena? Shouldn't Zig be more popular or is it still too small and immature?
- wyldfire 5y agoZig refers to itself with a version number < 1 and says that the language is not yet stable. That's going to filter the less adventurous crowd out right away. And that's probably ideal - zig can work out the kinks with the adventurous crowd and be better prepared for its post-1.0 life.
- rthomas6 5y agoI haven't used Zig, but my understanding is that Zig is more comparable to C, whereas Rust is more comparable to C++. Zig is a much smaller and simpler language, but Rust has more high level constructs and abstractions.
- sgt 5y agoI can't comment on either, but I do enjoy C and you can build relatively safe C programs using tools like Valgrind and static analysis. So unless you are building the next OpenSSL it could very well be worth sticking to C11.
- avianlyric 5y agoIf that was true then nobody would be fretting about unsafe memory access, and the primary source of bugs used in exploits wouldn’t exist. Unfortunately it isn’t true, C let’s you write plenty of broken programs that don't fail at run time, despite making a complete dogs breakfast of your systems memory space, instead we get crap like Heartbleed instead.
- usrbinbash 5y agoIts also perfectly possible to write broken programs in languages that take care of all memory related issues: var ch chan bool for i := 0; i < 10; i++ { go func() { // do something amazing ch<-true }() } Oops, I just leaked 10 goroutines, because I forgot to `make` the channel instead of declaring it, so its nil and will block indefinitely. Unless the program encounters an complete deadlock of all gorutines, this will not crash anything, and the caller of this code has no way of knowing it happened. Broken code can be written in any language, no matter how much checks & convenience it offers.
- zkldi 5y agoYou can write a forkbomb in any language -- that's besides the point. Some languages are easier to write broken code in than others.
- tialaramex 5y agoThis depends on deciding that "broken code" is all the same, but it isn't. Suppose I screw up writing the GIF decoder for avatars on my forum web site and you are a bad guy who can create accounts and upload an avatar. If I write the decoder in C it is very possible that your bad files can seize control of the web server, spill user credentials, post nonsense, mine crypto-currency on my servers, anything. If I write the decoder in safe Rust, some of these things are much harder to pull off, and I need to be really incompetent to cause the worst harm - but you can likely cause a lot of mayhem still if I screwed up. But if I write the decoder in WUFFS, the best you can achieve is to have the decoder chew CPU in an infinite loop or something. There are no boundary misses, integer overflows, or anything like that in WUFFS. My decoder might render your weird input as a giant orange splodge, or, as I said, spin forever wasting CPU, but it can't accidentally become a reverse shell server, or send you credentials from my password database, such things are entirely impossible. This is because WUFFS is a special purpose language. There doesn't need to be a way to write these nonsense programs in WUFFS, whereas it must be possible in a language like C to achieve the "general purpose" designator. But this should encourage us to write as little as possible with these unnecessarily powerful languages, like the way you don't use a chainsaw to sharpen pencils.
- dureuill 5y ago> The thing is, C also makes me aware of what is happening, by letting programs fail when I forget about certain things. Not necessarily. Many C programming errors invoke Undefined Behaviour, and then "appearing to work correctly" is a permissible option. So you will miss the programming error, until a change seemingly unrelated to that part of the system will trigger some segfaults or wrong results, or allow a determined attacker to use your program to launch calc.exe.
- mlindner 5y ago> The thing is, C also makes me aware of what is happening, by letting programs fail when I forget about certain things. That isn't true at all. The majority of my and other's bugs I hit working with C (I did C for several years at a large company) were because they explicitly did not fail when people forgot things. If you iteratively learn C you learn tons of bad behaviors as C does not tell you when things are broken.