Live data from Hacker News

Initialization in C++ is Seriously Bonkers

mikelui.io

121–130 of 130 posts

Re: Initialization in C++ is Seriously Bonkers

#121

Earlier quoted context omitted.

> The reason that stack variables are UNinitialized is (contra the article which thinks it's because the programmer didn't put an initializer in the source code) that memory is on the stack, which is allocated internal to your program and in practice was used previously by some other call for some other purpose. It is because the programmer didn't put an initializer into the source code. That's how the language is de…

"Any C programmer worth anything knows that this initializes i to an indeterminate value. This is wrong. Reading the variable does not simply give you an indeterminate value, it gives undefined behaviour" Undefined behavior in the technical sense is not acceptable in a language at all, let alone a feature. If you take the idea of undefined behavior seriously, then it is valid to blow up the world in response to an er…

First up, I think I was technically mistaken in saying the blog was technically mistaken. It seems the language spec defines the UB in terms of read-before-assignment, but they do use the term 'indeterminate value' as well. So the blog post can be seen as incomplete, rather than mistaken.

> Undefined behavior in the technical sense is not acceptable in a language at all

The C committee disagrees, and they have their reasons. They're aiming to maximise performance and support for various weird and wonderful platforms.

C's philosophy is not like that of Java where, say, an int is defined to be 32 bit and your machine just has to make it happen, even if it's a peculiar 48 bit machine or something.

> If you take the idea of undefined behavior seriously, then it is valid to blow up the world in response to an error and the compiler cannot protect you

Well sure. UB means you screwed up. C isn't a hand-holding language. Explode the whole process. Perfectly valid. Not without precedent in the C++ spec, incidentally; C++ is defined to explode your process if you screw up in a certain way with exceptions https://stackoverflow.com/a/43675980/

Your program can't literally blow up the world, of course, but that's beyond the scope of the language. If UB could result in nuclear apocalypse, that would mean a compiler bug could generate a binary that did the same thing, regardless of source language.

> Nobody would use C or C++ if they actually accepted the meaning of undefined behavior, so to program in these languages requires embracing doublethink.

Plenty of C/C++ programmers have a very sloppy attitude to undefined behaviour, and sometimes they get bitten by it. Aggressive optimising compilers like gcc, really do depend on you taking responsibility for writing code with defined behaviour. Break that contract, and you can see nightmare intermittent bugs that only appear when using certain compiler flags. See: http://blog.llvm.org/2011/05/what-every-c-programmer-should-...

Of course, this is all made worse by that it's impossible for static analysis to detect all instances of UB. If you want a language that doesn't take this attitude, you might try Ada. It's a pity more people don't.

Re: Initialization in C++ is Seriously Bonkers

#122
post #110

Earlier quoted context omitted.

"Any C programmer worth anything knows that this initializes i to an indeterminate value. This is wrong. Reading the variable does not simply give you an indeterminate value, it gives undefined behaviour" Undefined behavior in the technical sense is not acceptable in a language at all, let alone a feature. If you take the idea of undefined behavior seriously, then it is valid to blow up the world in response to an er…

>If you take the idea of undefined behavior seriously, then it is valid to blow up the world in response to an error and the compiler cannot protect you That's not its job. The compiler cannot protect you from everything. (You shouldn't be using a computer that has the ability to blow up the world.) The compiler merely translates code from language A into language B. If you pass it some code in some third language wh…

> If you pass it some code in some third language which could parse as mal-formed A code, then how is to detect that?

This 'other language' approach doesn't strike me as a good way of thinking about it. It misses the point that C is pretty unique in its broad use of undefined behaviour. Unlike Java, where everything has an unambiguous definition. (Well, ignoring plenty of platform-specific variation points in the standard library, such as file-path syntax.)

Re: Initialization in C++ is Seriously Bonkers

#124

Earlier quoted context omitted.

I can think of at least 3 reasons! 1) I don’t control the curriculum. 2) I’m pretty sure no one knows it well enough to teach. 3) Rust’s ecosystem and adoption is still too small for being taught as an engineering tool and for delivering employment opportunities to students. I personally like rust and hope it does well. I see it somewhat orthogonal to both C and C++

I’m pretty sure it’s not orthogonal. The goal is to replace both of these languages in the next 10+ years.

I understand Rust's goal is to have the zero-cost abstractions of C and C++ with greater safety, effectively displacing them.

This is obviously a personal opinion, but sometimes I like being able to create bugs in my code. Not from an industrial or business standpoint, but from a greater understanding POV. It's easier to reason about the underlying machine (yes, yes I know C/C++ models abstract machines) when I can actually break that machine with the tools at hand. There's unsafe Rust which I have not looked at, but in terms of getting my hands dirty, sometimes C/C++ just feels better. The primitiveness, even of template programming vs Haskell's typeclasses, or constexpr vs D's CTFE. Something about that raw primitiveness is attractive. This is absolutely positively probably just experience bias. I'm not sure if anyone else can relate to this.

So in that regard, I see Rust as orthogonal. If you want that feeling like, "hey, I'm just directly fiddling raw virtual memory addresses", that's not Rust's target. Rust markets itself as a safe language that hides all those bits by default.

Re: Initialization in C++ is Seriously Bonkers

#125
post #21

Earlier quoted context omitted.

It's hard to imagine many situations where you need to give a name to the constant value 0. And think how "optimal" it would be if there were many and we had a read-only BSS! We could cheaply get a huuuge section full of constant zeroes. I think you can in fact realize the idea in PECOFF, by the way. I still need to figure out some aspects to it these days (it seems a bit arcane and maybe Windows doesn't follow the s…

> I think you can in fact realize the idea in PECOFF, by the way. You can express it in ELF too, with a NOBITS segment with ALLOC but no WRITE flag. Whether that works or exercises bugs in the dynamic loader is an open question.

On Linux (in the kernel image), it exercises bugs in the dynamic loader. I tried it once :)

Re: Initialization in C++ is Seriously Bonkers

#126
post #110

Earlier quoted context omitted.

>If you take the idea of undefined behavior seriously, then it is valid to blow up the world in response to an error and the compiler cannot protect you That's not its job. The compiler cannot protect you from everything. (You shouldn't be using a computer that has the ability to blow up the world.) The compiler merely translates code from language A into language B. If you pass it some code in some third language wh…

> That's not its job. The compiler cannot protect you from everything. The compiler can protect us from many things, such as uninitialised variables. It’s just that we choose to define the semantics of the C compiler such that it doesn’t. Also I don’t think GP meant literally, physically destroy the world. It was a metaphor for catastrophic consequences of program misbehaviour. They meant that literally anything coul…

Right here is what I mean by doublethink. How can "literally anything" not include "literally physically destroy[ing] the world"? Of course, I was also using it as shorthand for any catastrophic consequence that is unacceptable.

It's the equivocation between inconsistent ideas that frustrates me. Of course, practically speaking you assume really bad things don't follow from undefined behavior. But then why the constant refrain about how we shouldn't rely on what actually happens?

I think I understand where the motivation for declaring undefined behavior comes from - people who set standards don't want responsibility for situations they don't completely control. But this disclaiming of responsibility puts the users of the standards in an impossible situation as a result.

I'm coming from a perspective of someone who programmed in C as my second language after BASIC, back in the 80s, before modern standards and before I knew anything about language standards. The philosophy and attitude of people who talk about standards and undefined behavior is something I first encountered on Usenet in the 90s, but I still am disturbed by it and haven't been "educated" to accept it.

Re: Initialization in C++ is Seriously Bonkers

#127

Earlier quoted context omitted.

Monads are still a little magical though.

I keep thinking that C could support monads with a little work.

As I see it, adding lambda/anonymous functions would have a transformative impact on the whole language. Adding them to C++ had a similar impact although the language offered some ways to get around the limitation.

Re: Initialization in C++ is Seriously Bonkers

#128

Earlier quoted context omitted.

I keep thinking that C could support monads with a little work.

As I see it, adding lambda/anonymous functions would have a transformative impact on the whole language. Adding them to C++ had a similar impact although the language offered some ways to get around the limitation.

Very true. When I look at C# pretty much all the cool stuff they have added relies on lambdas and closures. Same for JavaScript. It's the one feature that enables a ton of other features.

Re: Initialization in C++ is Seriously Bonkers

#129

Earlier quoted context omitted.

The parsing details can be complicated while the end result can be simple. Consider the actual C++ example that would be taught & used in practice: struct A { int i = 0; } int main() { A a; std::cout Hey, look, done. And you only had to teach a single thing - default initialization. Which has an obvious & simple syntax. The int i is always initialized to 0, as intended, and it's in a single spot at the point of decla…

> Which requires you to know that you can declare a type & an instance of that type in a single statement struct A { int i; }; const struct A default_A = { 0 }; Why didn't you try the obvious simple thing first? The whole undertaking is pointless in any case. Why would you need default values (i.e. templates for constructors) baked in? The only reasonable default value, sometimes, is all-zeroes (or all-ones...). Now,…

> The only reasonable default value, sometimes, is all-zeroes (or all-ones...).

That's very false. Consider a basic string container with a small-size optimization. The default value for capacity is neither 0 nor 1, but the size of the inline array.

Similarly it could be an enum value, and the default for a given class isn't whatever the 0 value happened to line up with because that's arbitrary anyway. An example being a basic type id of a fixed number of types.

Re: Initialization in C++ is Seriously Bonkers

#130
post #71

Earlier quoted context omitted.

Most pre-C++11 code I've seen that gets iterators from `begin()`, `end()`, and friends just typedef out their actual type anyways, so you the experience there is not that different from `auto`.

But those typedefs almost always (in my experience) contain some indication of at the very least an abbreviation of the type and some hints as to whether it's a pointer or not. That's a lot better to go on than "auto".

You already know the type if you're calling .begin() or .end() on it. You don't care about the type of the iterator because you know it can iterate over the collection you're working with.

That's what auto fixes. It avoids coupling the concept of iterating from the specific type of the iterator which isn't important.

Similarly auto helps you achieve DRY. 'auto myFoo = std::make_unique();'. The type wasn't removed, it just wasn't repeated twice.

But at this point auto, or things like auto, exist in nearly every major language. So you'll need to teach best practices for working with it at some point. That's a general thing that's everywhere.

Post reply on HN