7 ms·
Undefined Behavior in C and C++ (2024)
- VivaTechnics 1y agoWe switched to Rust. Generally, are there specific domains or applications where C/C++ remain preferable? Many exist—but are there tasks Rust fundamentally cannot handle or is a weak choice?
- imadr 1y agoI haven't used Rust extensively so I can't make any criticism besides that I find compilation times to be slower than C
- ost-ing 1y agoI find with C/++ I have to compile to find warnings and errors, while with Rust I get more information automatically due to the modern type and linking systems. As a result I compile Rust significantly less times which is a massive speed increase. Rusts tooling is hands down better than C/++ which aids to a more streamlined and efficient development experience
- bch 1y ago> Rusts tooling is hands down better than C/++ which aids to a more streamlined and efficient development experience Would you expand on this? What was your C tooling/workflow that was inferior to your new Rust experience?
- simonask 1y agoNot the GP, but the biggest one is dependency management. Cargo is just extremely good. As for the language tooling itself, static and runtime analyzers in C and C++ (and these are table stakes at this point) do not come close to the level of accuracy of the Rust compiler. If you care about writing unsafe code, Miri is orders of magnitude better at detecting UB than any runtime analyzer I've seen for C and C++.
- johnisgood 1y agoPacman is extremely good, too, for C. :)
- simonask 1y agoPacman solves a different problem. Cargo manages your project's dependencies, not system packages.
- johnisgood 1y agoI know, but often that is all you need for C.
- uecker 1y agoI do not think package management should be done at the level of programming languages.
- adwn 1y agoStrictly speaking, Cargo isn't part of the Rust programming language itself. Cargo is a layer on top of Rust, and you can use the Rust compiler completely independently of Cargo. I think bazel, for example, can compile and handle dependencies without Cargo.
- simonask 1y agoI agree, it should be done at the project level - that is, if you care about portability, reproducibility, deployment, etc.
- ykonstant 1y agoI also hear that Async Rust is very bad. I have no idea; if anyone knows, how does async in Rust compare to async in C++?
- 01HNNWZ0MV43FF 1y agoI am yet to use async in c++, but I did work on a multi threaded c++ project for a few years Rust is nicer for async and MT than c++ in every way. I am pretty sure. But it's still mid. If you use Rust async aggressively you will struggle with the borrow checker and the architecture results of channel hell. If you follow the "one control thread that does everything and never blocks" you can get far, but the language does not give you much help in doing that style neatly. I have never used Go. I love a lot of Go projects like Forgejo and SyncThing. Maybe Go solved async. Rust did not. C++ did not even add good tagged unions yet.
- ViewTrick1002 1y ago> I also hear that Async Rust is very bad. Not sure where this is coming from. Async rust is amazing as long as you only mix in one more hard concept. Be it traits, generics or whatever. You can confidently write and refactor heavily multithreaded code without being deathly afraid of race conditions etc. and it is extremely empowering. The problem comes when trying to write async generic traits in a multithreaded environment. Then just throwing stuff at the wall and hoping something sticks will quickly lead you into despair.
- kazinator 1y agoThe popular C compilers are seriously slow, too. Orders of magnitude compared to C compilers of yesteryear.
- uecker 1y agoAdvantages of C are short compilation time, portability, long-term stability, widely available expertise and training materials, less complexity. IMHO you can today deal with UB just fine in C if you want to by following best practices, and the reasons given when those are not followed would also rule out use of most other safer languages.
- lifthrasiir 1y ago> short compilation time > IMHO you can today deal with UB just fine in C if you want to by following best practices In the other words, short compilation time has been traded off with wetware brainwashing... well, adjustment time, which makes the supposed advantage much less desirable. It is still an advantage, I reckon though.
- uecker 1y agoI do not understand what you are tying to say, but it seems to be some hostile rambling.
- lifthrasiir 1y agoNever meant to be hostile (if I indeed were, I would have question every single word), but sorry for that. I mean to say that best practices do help much but learning those best practices take much time as well. So short compilation time is easily offseted by learning time, and C was not even designed to optimize compilation time anyway (C headers can take a lot to parse and discard even when unused!). Your other points do make much more sense and it's unfortunate that first points are destructively interfering each other, hence my comment.
- uecker 1y agoSorry, maybe I misread your comment. There are certainly languages easier to learn than C, but I would not say C++ or Rust fall into this category. At the same time, I find C compilation extremely fast exactly because of headers. In C you can split interface and implementation cleanly between header and c-file and this enables efficient incremental builds. In C++ most of the implementation is in headers, and all the template processing is order of magnitude more expensive than parsing C headers. Rust also does not seem to have proper separate compilation.
- mrheosuper 1y agoRust can do inline ASM, so finding a task Rust "fundamentally cannot handle" is almost impossible.
- eru 1y agoThat's almost as vacuous as saying that Rust can implement universal Turing machines are that Rust can do FFI?
- pizza234 1y agoYes, based on a few attempts chronicled in articles from different sources, Rust is a weak choice for game development, because it's too time-consuming to refactor.
- ramon156 1y agoWe've only had 6-7 years of hame dev in rust. Bevy is coming along nicely and will hopefully remove these pain points
- flohofwoe 1y ago"Mit dem Angriff Steiner's wird das alles in Ordnung kommen" ;) As shitty as C++ is from today's PoV, the entire gaming industry switched over within around 3 years towards the end of the 90s. 6..7 years is a long time, and a single engine (especially when it's more or less just a runtime without editor and robust asset pipeline) won't change the bigger picture that Rust is a pretty poor choice for gamedev.
- eru 1y ago> As shitty as C++ is from today's PoV, the entire gaming industry switched over within around 3 years towards the end of the 90s. Did they? What's your evidence? Are you including consoles? Btw, the alternatives in the 1990s were worse than they are now, so the bar to clear for eg C or C++ were lower.
- flohofwoe 1y agoI was there Gandalf... ;) Console SDKs offering C or C++ APIs doesn't really matter, because you can call C APIs from C++ just fine. So the language choice was a team and engine developer decision, not a platform owner decision (as it should be). From what I've seen, around the late mid-90's, C++ usage was still rare, right before 2000 it was already common and most middleware didn't even offer C APIs anymore. Of course a couple of years later Unity arrived and made the gamedev language choice more complicated again.
- bluetomcat 1y agoRust encourages a rather different "high-level" programming style that doesn't suit the domains where C excels. Pattern matching, traits, annotations, generics and functional idioms make the language verbose and semantically-complex. When you follow their best practices, the code ends up more complex than it really needs to be. C is a different kind of animal that encourages terseness and economy of expression. When you know what you are doing with C pointers, the compiler just doesn't get in the way.
- eru 1y agoPattern matching should make the language less verbose, not more. (Similar for many of the other things you mentioned.) > When you know what you are doing with C pointers, the compiler just doesn't get in the way. Alas, it doesn't get in the way of you shooting your own foot off, too. Rust allows unsafe and other shenanigans, if you want that.
- bluetomcat 1y ago> Pattern matching should make the language less verbose, not more. In the most basic cases, yes. It can be used as a more polished switch statement. It's the whole paradigm of "define an ad-hoc Enum here and there", encoding rigid semantic assumptions about a function's behaviour with ADTs, and pattern matching for control-flow. This feels like a very academic approach and modifying such code to alter its opinionated assumptions isn't funny.
- eru 1y agoHow is encoding all the assumptions and invariants badly in eg a bunch of booleans and nullable pointers any better?
- za_creature 1y ago> When you know what you are doing with C pointers, the compiler just doesn't get in the way. Tell me you use -fno-strict-aliasing without telling me. Fwiw, I agree with you and we're in good[citation needed] company: https://www.mail-archive.com/linux-btrfs@vger.kernel.org/msg01647.html https://www.mail-archive.com/linux-btrfs@vger.kernel.org/msg...
- mgaunard 1y agoRust forces you to code in the Rust way, while C or C++ let you do whatever you want.
- nicoburns 1y ago> C or C++ let you do whatever you want. C and C++ force you to code in the C and C++ ways. It may that that's what you want, but they certainly dont let me code how I want to code!
- mgaunard 1y agoThere is no C or C++ ways. It's widely known that every codebase is its own dialect.
- nicoburns 1y agoThere are lots of C and particularly C++ ways, but you're still restricted. Want to use methods in C: nope, you can't. Want language-level tagged unions and pattern matching in either language: nope. Same for guaranteed tail call optimisation and a bunch of other things. This is especially true for C which supports almost nothing (it doesn't even have a sensible array type!). But is also true for C++: while it supports a lot, it doesn't support everything.
- bigfishrunning 1y agowhat changes, in your opinion, would need to be made to the C array type to make it "sensible"? C's array is simplistic, but I don't think it's not "sensible"...
- mgaunard 1y agoconsider the C++ std::array, which exists to make arrays behave like normal objects. You can do the same in C by wrapping your array in a struct.
- 1y ago
- pjmlp 1y agoYes, all the industries where C and C++ are the industry standards like Khronos APIs, POSIX, CUDA, DirectX, Metal, console devkits, LLVM and GCC implementation,.... Not only you are faced with creating your own wrappers, if no one else has done it already. The tooling, for IDEs and graphical debuggers, assumes either C or C++, so it won't be there for Rust. Ideally the day will come where those ecosystems might also embrace Rust, but that is still decades away maybe.
- m-schuetz 1y agoPrototyping in any domain. It's nice to do some quick&dirty way to rapidly evaluate ideas and solutions.
- eru 1y agoI don't think C nor C++ were ever great languages for prototyping? (And definitely not better than Rust.)
- m-schuetz 1y agoPlease try not to be obnoxious and turn this into a language war.
- eru 1y agoHow is this obnoxious? C and C++ have their strengths, but rapid prototyping is generally not seen to be amongst them. This shouldn't be any more controversial than saying that pure Python is generally slow.
- m-schuetz 1y agoThey are pretty much the best choice for prototyping 3D apps and GPU algorithms. They're fast, powerful, and don't impose restrictions - you can do whatever and however. It also helps that CUDA is C++.
- eru 1y ago> Generally, are there specific domains or applications where C/C++ remain preferable? Well, anything were your people have more experience in the other language or the libraries are a lot better.
- teunispeters 1y agoembedded hardware, any processor Rust doesn't support (there are many), and any place where code size is critical. Rust has a BIG base size for an application, uselessly so at this time. I'd also love to see if it offered anything that could be any use in those spaces - especially where no memory allocation takes place at all. C (and to a lesser extent C++) are both very good in those spaces.
- steveklabnik 1y agoYou can absolutely make small rust programs, you just have to actually configure things the right way. Additionally, the Rust language doesn’t have allocation at all, it’s purely a library concern. If you don’t want heap allocations, then don’t include them. It works well. The smallest binary rustc has produced is like ~145 bytes.
- teunispeters 1y agoThat is far from my only concern. But it's good to see Rust is finally paying attention to binary sizes. And the overwhelming complexity of rust code is definitely not a gain when one is working in embedded spaces anyway. I am however really REALLY annoyed with the aggressive sales tactics of the rust community.
- steveklabnik 1y ago> But it's good to see Rust is finally paying attention to binary sizes. Just to be clear, this isn't a recent development, it has been this way for many years at this point.
- jandrewrogers 1y agoAn application domain where C++ is notably better is when the ownership and lifetimes of objects are not knowable at compile-time, only being resolvable at runtime. High-performance database kernels are a canonical example of code where this tends to be common. Beyond that, recent C++ versions have much more expressive metaprogramming capability. The ability to do extensive codegen and code verification within C++ at compile-time reduces lines of code and increases safety in a significant way.
- mckravchyk 1y agoIf you wanted to develop a cross-platform native desktop / mobile app in one framework without bundling / using a web browser, only QT comes to mind, which is C++. I think there are some bindings though.
- fattah25 1y agoRust here rust there. We are just talking about C not rust. Why we have to using rust. If you talking memory safety why there is no one recommends Ada language instead of rust. We have zig, Hare, Odin, V too.
- ViewTrick1002 1y ago> Ada language instead of rust Because it never achieved mainstream success? And Zig for example is very much not memory safe. Which a cursory search for ”segfault” in the Bun repo quickly tells you. https://github.com/oven-sh/bun/issues?q=is%3Aissue%20state%3Aopen%20segfault https://github.com/oven-sh/bun/issues?q=is%3Aissue%20state%3...
- lifthrasiir 1y agoMore accurately speaking, Zig helps spatial memory safety (e.g. out-of-bound access) but doesn't help temporal memory safety (e.g. use-after-free) which Rust excels at.
- ViewTrick1002 1y agoAs long as you are using the "releasesafe" build mode and not "releasefast" or "releasesmall".
- pjmlp 1y agoWhich is something that even PL/I predating C already had.
- johnisgood 1y ago> Because it never achieved mainstream success? And with this attitude it never will. With Rust's hype, it would.
- pjmlp 1y agoNone of them solve use after free, for example. Ada would rather be a nice choice, but most hackers love their curly brackets.
- uecker 1y agoOne has to add that from the 218 UB in the ISO C23, 87 are in the core language. From those we already removed 26 and are in progress of removing many others. You can find my latest update here (since then there was also some progress): https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3529.pdf https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3529.pdf
- ncruces 1y agoAnd yet, I see P1434R0 seemingly trying to introduce new undefined behavior, around integer-to-pointer conversions, where previously you had reasonably sensible implementation defined behavior (the conversions “are intended to be consistent with the addressing structure of the execution environment"). https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1434r0.html https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p14...
- gpderetta 1y agoPointer provenance already existed before, but the standards were contradictory and incomplete. This is an effort to more rigorously nail down the semantics. i.e., the UB already existed, but it was not explicit had to be inferred from the whole text and the boundaries were fuzzy. Remember that anything not explicitly defined by the standard, is implicitly undefined. Also remember, just because you can legally construct a pointer it doesn't mean it is safe to dereference.
- JonChesterfield 1y agoPointer provenance was certainly not here in the 80s. That's a more modern creation seeking to extract better performance from some applications at a cost of making others broken/unimplementable. It's not something that exists in the hardware. It's also not a good idea, though trying to steer people away from it proved beyond my politics.
- gpderetta 1y agoI'm not a compiler writer, but I don't know how you would be able to implement any optimization while allowing arbitrary pointer forging and without whole-program analysis.
- kazinator 1y agoIn C, using uninitialized data is undefined behavior only if: - it is an automatic variable whose address has not been taken; or - the uninitialized object' bits are such that it takes on a non-value representation.
- laauraa 1y ago>Uninitialized data They at least fixed this in c++26. No longer UB, but "erroneous behavior". Still some random garbage value (so an uninitialized pointer will likely lead to disastrous results still), but the compiler isn't allowed to fuck up your code, it has to generate code as if it had some value.
- kazinator 1y agoC also fixed it in its way. Access to an uninitialized object defined in automatic storage, whose address is not taken, is UB. Access to any uninitialized object whose bit pattern is a non-value, likewise. Otherwise, it's good: the value implied by the bit pattern is obtained and computation goes on its merry way.
- tialaramex 1y agoIt won't be a "random garbage value" but is instead a value the compiler chose. In effect if you don't opt out your value will always be initialized but not to a useful value you chose. You can think of this as similar to the (current, defanged and deprecated as well as unsafe) Rust std::mem::uninitialized() There were earlier attempts to make this value zero, or rather, as many 0x00 bytes as needed, because on most platforms that's markedly cheaper to do, but unfortunately some C++ would actually have worse bugs if the "forgot to initialize" case was reliably zero instead.
- eru 1y agoWhat are these worse bugs?
- tialaramex 1y agoThe classic thing is, we're granting user credentials - maybe we're a login proces, or a remote execution helper - and we're on Unix. In some corner case we forget to fill out the user ID. So it's "random noise". Maybe in the executable distributed to your users it was 0x4C6F6769 because the word "Login" was in that memory in some other code and we never initialized it so... Bad guys find the corner case and they can now authenticate as user 0x4C6F6769 which doesn't exist and so that's useless. But - when we upgrade to C++ 26 with the hypothetical zero "fix" now they're root instead!
- kazinator 1y agoUndefined behavior only means that ISO C doesn't give requirements, not that nobody gives requirements. Many useful extensions are instances where undefined behavior is documented by an implementation. Including a header that is not in the program, and not in ISO C, is undefined behavior. So is calling a function that is not in ISO C and not in the program. (If the function is not anywhere, the program won't link. But if it is somewhere, then ISO C has nothing to say about its behavior.) Correct, portable POSIX C programs have undefined behavior in ISO C; only if we interpret them via IEEE 1003 are they defined by that document. If you invent a new platform with a C compiler, you can have it such that #include <windows.h> reformats all the attached storage devices. ISO C allows this because it doesn't specify what happens if #include <windows.h> successfully resolves to a file and includes its contents. Those contents could be anything, including some compile-time instruction to do harm. Even if a compiler's documentationd doesn't grant that a certain instance of undefined behavior is a documented extension, the existence of a de facto extension can be inferred empirically through numerous experiments: compiling test code and reverse engineering the object code. Moreover, the source code for a compiler may be available; the behavior of something can be inferred from studying the code. The code could change in the next version. But so could the documentation; documentation can take away a documented extension the same way as a compiler code change can take away a de facto extension. Speaking of object code: if you follow a programming paradigm of verifying the object code, then undefined behavior becomes moot, to an extent. You don't trust the compiler anyway. If the machine code has the behavior which implements the requirements that your project expects of the source code, then the necessary thing has been somehow obtained.
- pjmlp 1y agoUnfortunely it also means that when the programmer fails to understand what undefined behaviour is exposed on their code, the compiler is free to take advantage of that to do the ultimate performance optimizations as means to beat compiler benchmarks. The code change might come in something as innocent as a bug fix to the compiler.
- account42 1y agoAh yes, the good old "compiler writers only care about benchmarks and are out to hurt everyone else" nonsense. I for one am glad that compilers can assume that things that can't happen according to the language do in fact not happen and don't bloat my programs with code to handle them.
- pizlonator 1y agoI don’t buy the “it’s because of optimization argument”. And I especially don’t buy that UB is there for register allocation. First of all, that argument only explains UB of OOB memory accesses at best. Second, you could define the meaning of OOB by just saying “pointers are integers” and then further state that nonescaping locals don’t get addresses. Many ways you could specify that, if you cared badly enough. My favorite way to do it involves saying that pointers to locals are lazy thunks that create addresses on demand.
- j16sdiz 1y ago> First of all, that argument only explains UB of OOB memory accesses at best. It explains many loop-unroll and integer overflow as well.
- tialaramex 1y ago> Second, you could define the meaning of OOB by just saying “pointers are integers" This means losing a lot of optimisations, so in fact when you say you "don't buy" this argument you only mean that you don't care about optimisation. Which is fine, but this does mean the "improved" C isn't very useful in a lot of applications, might as well choose Java.
- pizlonator 1y ago> This means losing a lot of optimisations You won’t lose “a lot” of optimizations and you certainly won’t lose enough for it to make a noticeable difference in any workload that isn’t SPEC
- gpderetta 1y ago> nonescaping locals don’t get addresses inlining, interprocedural optimizations. For example, something as an trivial accessor member function would be hard to optimize.
- pjmlp 1y agoSafer languages manage similar optimizations without having to rely on UB.
- safercplusplus 1y agoA couple of solutions in development (but already usable) that more effectively address UB: i) "Fil-C is a fanatically compatible memory-safe implementation of C and C++. Lots of software compiles and runs with Fil-C with zero or minimal changes. All memory safety errors are caught as Fil-C panics." "Fil-C only works on Linux/X86_64." ii) "scpptool is a command line tool to help enforce a memory and data race safe subset of C++. It's designed to work with the SaferCPlusPlus library. It analyzes the specified C++ file(s) and reports places in the code that it cannot verify to be safe. By design, the tool and the library should be able to fully ensure "lifetime", bounds and data race safety." "This tool also has some ability to convert C source files to the memory safe subset of C++ it enforces"
- tialaramex 1y agoFil-C is interesting because as you'd expect it takes a significant performance penalty to deliver this property, if it's broadly adopted that would suggest that - at least in this regard - C programmers genuinely do prioritise their simpler language over mundane ideas like platform support or performance. The resulting language doesn't make sense for commercial purposes but there's no reason it couldn't be popular with hobbyists.
- eru 1y agoWell, you could also treat Fil-C as a sanitiser, like memory-san or ub-san: Run your test suite and some other workloads under Fil-C for a while, fix any problems report, and if it doesn't report any problems after a while, compile the whole thing with GCC afterwards for your release version.
- safercplusplus 1y agoRight. And of course there are still less-performance-sensitive C/C++ applications (curl, postfix, git, etc.) that could have memory-safe release versions. But the point is also to dispel the conventional wisdom that C/C++ is necessarily intrinsically unsafe. It's a tradeoff between safety, performance and flexibility/compatibility. And you don't necessarily need to jump to a completely different language to get a different tradeoff. Fil-C sacrifices some performance for safety and compatibility. The traditional compilers sacrifice some safety for performance and flexibility/compatibility. And scpptool aims to provide the option of sacrificing some flexibility for safety and performance. (Along with the other two tradeoffs available in the same program). The claim is that C++ turns out to be expressive enough to accommodate the various tradeoffs. (Though I'm not saying it's always gonna be pretty :)
- roman_soldier 1y agoJust use Zig, it fixes all this
- grougnax 1y agoWorse languages ever.
- OskarS 1y agoC and C++ are languages that brought us UNIX, the Linux kernel, macOS and Windows, the interpreters of virtually every other language in the world, powering virtually all software in the world as well as the vast majority of embedded devices. Chill the fuck out.
- compiler-guy 1y agoJack Sparrow: “… but you have heard of them.” The dustbin of programming languages is jam packed with elegant, technically terrific, languages that never went anywhere.
- account42 1y agoExcept for all the others.
- IshKebab 1y agoThis asserts that UB was deliberately created for optimisation purposes; not to handle implementation differences. It doesn't provide any evidence though and that seems unlikely to me. The spec even says: > behavior, upon use of a nonportable or erroneous program construct or of erroneous data, for which this International Standard imposes no requirements No motivation is given that I could find, so the actual difference between undefined and implementation defined behaviour seems to be based on whether the behaviour needs to be documented.
- flohofwoe 1y agoI'd say the original intent of UB was not the sort of "optimizer exploits" we see today, but to allow wiggle room for supporting vastly different CPUs without having to compromise runtime performance or increasing compiler complexity to balance performance versus correctness. Basically an escape hatch for compilers. The difference to IB also has always been quite fuzzy. Also the C spec has always been a pragmatic afterthought, created and maintained to establish at least a minimal common feature set expected of C compilers. The really interesting stuff still only exists outside the spec in vendor language extensions.
- agent327 1y agoI, once again, disagree with the premise that UB is a necessary precondition for optimisation, or that it exists to allow for optimisation. You do not need UB to unroll a loop, inline a function, lift an object or computation out of a loop, etc. Moreover, _most_ UB does not assist in optimisation at all. The two instances where UB allows for optimisation are as follows: 1. The 'signed overflow' UB allows for faster array indexing. By ignoring potential overflow, the compiler can generate code that doesn't check for accidental overflow (which would require masking the array index, recomputing the address on each loop iteration). I believe the better solution here would be to introduce a specific type for iterating over arrays that will never overflow; size_t would do fine, and making signed overflow at least implementation defined, if not outright fully defined, after a suitable period during which compilers warn if you use a too-small type for array indexing. 2. The 'aliasing' UB does away with the need to read/write values to/from memory each time they're used, and is extremely important to performance optimisation. But the rest? Most of it does precisely nothing for performance. At 'best', the compiler uses detected UB to silently eliminate code branches, but that's something to be feared, not celebrated. It isn't an optimisation if it removes vital program logic, because the compiler could 'demonstrate' that it could not possibly take the removed branch, on account of it containing UB. The claim in the linked article ("what every C programmer should know") that use of uninitialized variables allows for additional optimisation is incorrect. What it does instead is this: if the compiler see you declare a variable, and then reading from it before writing to it, it has detected UB, and since the rule is that "the compiler is allowed to assume UB does not occur", use that as 'evidence' that that code branch will never occur and can be eliminated. It does not make things go faster; it makes them go _wrong_. Undefined behaviour, ultimately, exists for many reasons: because the standards committee forgot a case, because the underlying platforms differ too wildly, because you cannot predict in advance what the result of a bug may be, to grandfather in broken old compilers, etc. It does not, in any way, shape, or form, exist _in order to_ enable optimisation. It _allows_ it in some cases, but that is, and never was, not the goal. Moreover, the phrasing of "the compiler is allowed to assume that UB does not occur" was originally only meant to indicate that the compiler was allowed to emit code as if all was well, without introducing additional tests (for example, to see if overflow occurred or if a pointer was valid) - clearly that would be very expensive or downright infeasible. Unfortunately, over time this has enabled a toxic attitude to grow that turns minor bugs into major disasters, all in the name of 'performance'. The two bullet points towards the end of the article are both true: the compiler SHOULD NOT behave like an adversary, and the compiler DOES NEED license to optimize. The mistake is thinking that UB is a necessary component of such license. If that were true, a language with more UB would automatically be faster than one with less. In reality, C++ and Rust are roughly identical in performance.
- agalunar 1y agoA small nit: the development of Unix began on the PDP-7 in assembly, not the PDP-11. (The B language was implemented for the PDP-7 before the PDP-11, which are rather different machines. It’s sometimes suggested that the increment and decrement operators in C, which were inherited from B, are due to the instruction set architecture of the PDP-11, but this could not have been the case. Per Dennis Ritchie:¹ > Thompson went a step further by inventing the ++ and -- operators, which increment or decrement; their prefix or postfix position determines whether the alteration occurs before or after noting the value of the operand. They were not in the earliest versions of B, but appeared along the way. People often guess that they were created to use the auto-increment and auto-decrement address modes provided by the DEC PDP-11 on which C and Unix first became popular. This is historically impossible, since there was no PDP-11 when B was developed. The PDP-7, however, did have a few “auto-increment” memory cells, with the property that an indirect memory reference through them incremented the cell. This feature probably suggested such operators to Thompson; the generalization to make them both prefix and postfix was his own. Another person puts it this way:² > It's a myth to suggest C’s design is based on the PDP-11. People often quote, for example, the increment and decrement operators because they have an analogue in the PDP-11 instruction set. This is, however, a coincidence. Those operators were invented before the language [i.e. B] was ported to the PDP-11. In any case, the PDP-11 usually gets all the love, but I want to make sure the other PDPs get some too!) [1] https://www.bell-labs.com/usr/dmr/www/chist.html https://www.bell-labs.com/usr/dmr/www/chist.html [2] https://retrocomputing.stackexchange.com/questions/8869 https://retrocomputing.stackexchange.com/questions/8869