3 ms·
Totally agree with your post. I don't know what your conclusion is, though. My conclusion is that C lets me do what I want straightforwardly, I still haven't f
by jstimpfle 5y ago
Totally agree with your post. I don't know what your conclusion is, though.
My conclusion is that C lets me do what I want straightforwardly, I still haven't found this quality in a different language (there is inertia, I've only tried Zig for an hour for example and gave up in frustration). Also, to some degree I understand where most UB definitions are coming from and even agree with some of them.
The basic idea behind UB is to give the compiler a free pass (optimization, and not having to detect corruption) if the program has a bug anyway.
In some cases there can be legitimate surprises, when the programmer made some assumptions about guarantees that C can't give and that a newer language (without the historical baggage of supporting some architecture) can give, which might result in the compiler transforming a little bug into something bigger (more dangerous?). An example that I've personally learned at some point is integer shifting and masking in conjunction with implicit type promotion to int (which is signed).
All in all though, while I'm not be the most sophisticated programmer and most of my programms are never exposed to hacking attempts, just anecdotally I've had very, very little headaches from UB.
- vlovich123 5y ago> My conclusion is that C lets me do what I want straightforwardly, I still haven't found this quality in a different language C is the language where you've found things to be straightfoward? I'm curious what you're working on where this is the case. I'd reach for Python, Go, Java, Swift, C#, depending on what I'm trying to do (even C++ can be better, but it comes with its own unique additional set of footguns so some C acolytes tend to shy away from it). For most projects (at least those starting from scratch today) I'd probably choose Rust if C/C++ would otherwise be the only appropriate choice & use Python/Go/Swift/Java/C# elsewhere. > All in all though, while I'm not be the most sophisticated programmer and most of my programms are never exposed to hacking attempts, just anecdotally I've had very, very little headaches from UB. and > An example that I've personally learned at some point is integer shifting and masking in conjunction with implicit type promotion to int (which is signed). Is an interesting mix of statements. I can say that I've worked on quite a few large codebases with a myriad of contributors, & I've always uncovered UB when I went looking for it. Some of it harmless, others less so. It's a "this probably is invisible 90% of the time you run it locally" kind of thing, so unless you have thorough tests you can run under UBSAN, you're likely just unaware of the scope of the problem. That being said, I don't want to blow it out of proportion. I'd say memory safety & thread safety are bigger classes of issues. As with UB, if you're not running sanitizers with very large test suites, you're unlikely to see it [1][2]. As for why UB is a real problem conceptually, this used to be well-formed and correctly behaving pre-C99: ``` uint8_t x = <pointer to some buffer> uint64_t y = (uint64_t)x; ``` I don't mean in a "this just happened to work" but in a "this was valid semantics of the language". Now it requires a memcpy. It's also entirely possible this is valid in C & not C++? That's what I mean by UB being very subtle. Yes, I understand why UB is important to allow compilers to generate more optimal code. I think Rust strikes a better balance here - most code doesn't* need that tradeoff and where it's important can be explicitly annotated with that fact. Ultimately, to me the fact that even the Linux kernel is subject to these kinds of issues[3], gives me enough pause to believe it's a real problem in the ecosystem (among many others). So other, safer languages, are usually better if you can use them. Most experienced developers should have a number of languages under their belts they're fluent in and be able to switch to languages they haven't seen before (albeit there's always a cost & it's not fun, so I don't seek it out as often as I maybe should). [1] https://reviews.llvm.org/D51170 https://reviews.llvm.org/D51170 [2] https://bugs.llvm.org/show_bug.cgi?id=23293 https://bugs.llvm.org/show_bug.cgi?id=23293 [3] https://software.intel.com/content/www/us/en/develop/blogs/null-pointer-dereferencing-causes-undefined-behavior.html https://software.intel.com/content/www/us/en/develop/blogs/n...
- jstimpfle 5y agoYou can escape stars with backslash so they won't be interpreted as a section in italics: *** can be achieved like this: \\*\\*\\* > C is the language where you've found things to be straightfoward? I'm curious what you're working on where this is the case. Recently I've done networking, threading, embedded, GUI (2D/3D), compilers/interpreters - most is completely from scratch. I'm not a world-class expert in any of these domains but I'd like to think that I'm decent, especially last year I've finally made a leap in my understanding of systems design. In general, the stuff that I manage to complete gets exceptionally few bug reports and has good performance without any optimization. I'm not claiming that my statements have any scientific accuracy, just that C is what turned out to be subjectively the most versatile, productive, and fun one for me - even though (or because) it meant going through a steep learning curve, learning how to do most things from scratch. I've tried quite a few languages before, I have for example significantly invested in: bash/sh, python, Javascript, "modern" C++, Haskell. And coded in a few others, like Java, C#, Postscript, Erlang, Perl... Today I'm still doing some Python for some scripting / admin stuff, but I don't enjoy it anymore. If I had to find one reason why I don't like those languages, it's that they make it so hard/impossible or obscure to do the simplest thing that a program needs to do: Copy data. Shaping the code and data architecture to the problem from the ground up brings wins in the long run. It seems to me like most languages optimize for brevity and cleverness in toy examples or short script-like programs, while larger architectures simply don't profit from the conventions or syntax that the more "convenient" languages set up - beyond a few thousand lines of code those are only getting in the way. I don't need one predefined way how to do "objects" or "interfaces" - the preexisting stuff is usually not a good solution to the problem. It's better approaching problems by looking at what data representations are needed and how can they be transformed between each other, and this stuff can usually be achieved directly by mostly copying data around. C doesn't even encourage the programmer to try anything more complex, and this tends to result in programs that solve the problem in a straightforward way.