
Initialization in C++ is Seriously Bonkers - signa11
https://mikelui.io/2019/01/03/seriously-bonkers.html
======
ajross
> [Why is a global variable zero?] Because i has static storage duration, it’s
> initialized to unsigned zero. Why, you ask? Because the standard says so.

No, it's because i lives in an area allocated at the OS level, and OS has to
have initialized that memory with _something_ (otherwise you'd have a security
bug). The .bss segment has been zeroed, for obvious reasons, since the advent
of modern linkage.

The explanation is backwards. The reason that stack variables are _UN_
initialized 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.

The fact that stack variables are uninitialized by default is actually an
intentional feature, not a bug, as it correctly expresses the behavior of the
runtime environment. Now, it may not be a _good_ feature, and modern language
designs have omitted it (by, let's remember, relying on much better modern
compilers to elide all the extra code that would otherwise have been needed to
zero out a stack frame!).

But it's got nothing to do with the syntax of the language. You can rely on
.bss being zero in assembly, and similarly be surprised that stack memory is
going to have junk in it.

~~~
lasagnaphil
I always think C/C++ is not a good first language to teach, because you need
to understand some general concepts about computer systems before diving in.
Probably more than half of the beginners in their first C class give up when
handling pointers/arrays - it's not because the concept is hard, but because
they have no idea what the "memory" in the computer actually is and how it
works. You're left with a naive analogy of home addresses and phone numbers,
but it really doesn't give pointers justice (especially when dealing with
arrays and dynamic memory!) Every student learning C should have a basic idea
about the stack and the heap, how function calls work, and how assembly code
is executed, in order to get the full picture.

Anyways, the problem C++ has for beginners is that the language tries to
obscure the already hard to understand low-level behavior with high-level
concepts (to be fair, that is the language's ultimate goal: to provide a high-
level abstraction for low-level code.) So maybe good for your second/third
language, but definitely not your first.

~~~
williamdclt
I learnt C as a first language, and those computer sytems concept were
explained as we go. It's not that hard to understand what memory is and that
variables values are stored there, and I've had no problem with pointers.

And the big advantage is that when you understand those concept and pointers,
there's no magic in other languages (value/reference parameters, objects,
functions as first-class citizens)

~~~
maxxxxx
Totally agree. Knowing C and a little assembly takes out the magic from other
higher level languages. That's a good thing.

~~~
angry_octet
Monads are still a little magical though.

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

~~~
geezerjay
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.

~~~
maxxxxx
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.

------
tines
In my view this author falls into the camp of people calling C++ a mess
because it gives them too much control when they ask for it. One of his
examples which he calls "The Abyss":

    
    
        #include <iostream>
        struct A {
            A(std::initializer_list<int> l) : i(2) {}
            A(int i = 1) : i(i) {}
            int i;
        };
        int main() {
            A a1;
            A a2{};
            A a3(3);
            A a4 = {5};
            A a5{4, 3, 2};
            std::cout << a1.i << " "
                      << a2.i << " "
                      << a3.i << " "
                      << a4.i << " "
                      << a5.i << std::endl;
        }
        

which outputs:

    
    
        1 1 3 2 2
    

he claims to be mysterious but is actually pretty reasonable.

In the case of:

a1: There is no initializer list in the variable declaration, so ctor 2 is
called.

a2: An empty initializer list should reasonably behave like a default
constructor, and a default constructor should be more efficient than
processing an initializer_list, so ctor 2 is called. In general, the
constructor with the matching number of arguments and correct types is
preferred over initializer_list constructors. Makes sense, being able to
specialize on number and type of arguments is more powerful than a design
where a single initializer_list ctor invalidates all other ctors.

a3: No list, ctor 2 is called.

a4: Non-empty init list and no specialized non-init-list ctor, so ctor 1 is
called.

a5: Several-variable init list and no overriding 3-variable constructor with
matching types, therefore ctor 1 is called.

Complex? Maybe, but that's what you get with C++: very fine control of program
semantics, benefit being expressive libraries. No other language fills this
niche that I'm aware of.

I agree that C++ isn't a good language to teach in a CS 101 class. But no
other single language is good either. The goal of CS 101 is to not make
students give up before they get hooked, and that can happen because the
material is too challenging or not challenging enough. For people in the
former category, give them a scripting language and visual feedback, like Lua
+ Garrysmod. For the latter, give them assembly, haskell, C, C++ (teaching it
like "C with templates" not "C with classes").

~~~
ori_b
> _Complex? Yes, but that 's what you get with C++:_

Congrats on successfully summarizing the complaint.

It's not mysterious, but it's gratuitously complex. And this is only variable
initialization.

~~~
jblow
Indeed.

If just setting integers is this complicated, what do you expect to happen
when you are trying to solve real problems?

~~~
kllrnohj
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 << a.i << std::endl;
       }
    

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
declaration.

Now try doing that in C. Oh, wait, you can't. The closest you can get is this
mess:

    
    
       struct A {
         int i;
       } const default_A = {0};
       
       int main() {
         struct A a5 = default_A;
         printf("%d\n", a5.i);
       }
    

Which requires you to know that you can declare a type & an instance of that
type in a single statement, why that const is where it is and why that's
important here, how braced initialization rules work, that you need to always
remember to manually initialize to your default_A, and that %d means 'int'.
And the compiler won't help you with any of this except for the %d part if you
get it wrong.

~~~
jstimpfle
> 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, constant _data_ (i.e. things that contain more useful information than
just "it's the default because a real value is missing" \-- and that aren't
copied around pointlessly) is another case, and C's plain old value
initializer syntax (as demonstrated above) serves it perfectly well.

~~~
kllrnohj
> 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.

------
kllrnohj
> C++ is not a language I’d want to teach beginners. At no point in this post
> was there room for systems programming concepts, discourse on programming
> paradigms, computational-oriented problem solving methodologies, or
> fundamental algorithms.

This is a ridiculous argument. The entire point of the post was, as the author
even noted, purely to deep dive into a rabbit hole, get super picky & pedantic
about standards wording so that you can act _surprised_ that copy constructors
exist in C++ while it was completely glossed over in the identical C example
of a struct copy initialization, and almost none of this is useful to know.

So don't fucking teach it and you'd have plenty of time to cover all that
other stuff. Bam, problem solved. Ignore pre-C++11 entirely, and purely teach
& use the new stuff which fixes all the complexity, and leave the rabbit hole
for people that care about exploring the past.

Oh, and don't use standards wording because those are for compilers to use to
implement the language, not for programmers to understand how to use it
effectively. The only time it's ever useful to know the full definition of an
aggregate type or how it has changed is when you want to write a blog post
calling them crazy or complex. It's never useful to know when _using_ the
language. And if you have an object that cares to enforce it just

    
    
        static_assert(std::is_aggregate_v<A>, "A isn't an aggregate type");
    

Tada, now the compiler will tell you if/when you violated the rule and you
don't need to try and understand the full scope of the ruleset.

~~~
MikeLui
Hi. I appreciate your candor, although I'm not sure what I've done to inspire
such contempt. You're correct that the entire point of the post was to deep
dive into a rabbit hole. I acknowledged that and also acknowledged that the
standardese is unnecessary most of the time, to avoid any miscommunication in
my intent. I did this point out how large the language is and, more to the
point, to point out how _potentially_ complex it can be, compared to C. If
you've ever had to spend entire weekends running through students' broken C
code at scale, and see all the interesting ways one can complicate seemingly
simple things, then I think you'd agree that giving them _more_ ways to
confuse themselves in a fast-paced academic environment is not the way
forward. If you _have_ had that experience and didn't have any problems, then
you are fortunate to work with such fast learners! (Not to imply the students
I've had are dumb in anyway, many are very bright!)

~~~
MichaelMoser123
I am not sure about the following issue: What would make your students more
productive?

    
    
       - use C++, given that you stick to some convenient subset of C++ and can use stl
       - stick with C and force them to do the basics, like linked lists and string abstractions over and over again.
    

I guess in the olden days really good students used to develop their own
library of C abstractions, and reuse them with several courses; but you can't
quite do that if you have C in just one course and what's the point anyway in
this day and age ?

~~~
MikeLui
How else would they learn about linked lists and dynamic arrays?

~~~
MichaelMoser123
depends on the course, if its a data structure course then its fine to do the
linked list and dynamic array, but forcing your students to do them again and
again in later courses will not make them very productive, they also might not
enjoy the experience too much.

This could happen for example if the data structure course was in python or
java and later courses are in C.

Also you can do the darn list in many ways: single linked list, double, ring,
with counter/without counter. All very important in this day and age when you
should know to avoid them altogether because linked lists fucks up cache
behavior.

~~~
MikeLui
Funny you mention that: the data structures course I took let us use any
language we wanted. I chose python because I thought “hey python’s easier than
C!” So I started making some tree implementation from scratch, and I kept
having problems because I didn’t understand Python’s name binding system at
the time, because I had no mental model of pointers or references or objects
really. (Of course it was a shallowcopy/deepcopy issue). The next assignment I
did in C and it was much more natural. Only later did I realize how meta and
absurd it was to build custom dictionaries out of python primitives.

~~~
MichaelMoser123
a dictionary in python where everything is a dictionary. That's what
undergraduate courses are made for!

------
lordnacho
One thing that's related to syntax and initialization has annoyed me and many
other c++ coders for a while:

[https://en.wikipedia.org/wiki/Most_vexing_parse](https://en.wikipedia.org/wiki/Most_vexing_parse)

And it's one of the motivations for the {} syntax.

------
MikeLui
Author here--some extra context to add:

The post was mostly written to point my students to, so I don't have to keep
repeating myself. I get a not-insignificant number of 1st, 2nd, and 3rd years
(in a 5-year program) believing C is some antiquated language and believing
that they're getting held back in some way by learning C vs C++. One even
suggested the department was incompetent for not teaching C++. Because I work
in an engineering department, many students have not had a great deal of time
learning programming fundamentals early on and regardless, they are eager to
learn more advanced tools than they are ready to use. It is a bit rambling for
the purpose--I have a tendency to...erm, overwhelm...with information to make
my point.

I haven't blogged much so I originally submitted it at lobste.rs[1] for any
advice on the writing and visual style of the site. I welcome any constructive
feedback. E.g. "I hate that side-nav! It keeps popping in and out!"

Also to clarify, I _am not_ anti-C++ is anyway. I am a firm practitioner of
Chesterton's fence[2] and believe in nuance. That cuts both ways. C++ is the
way it is because it filled a specific need. It's greatest flaw is trying to
appease everyone and, recently, trying to catch up quickly to recent QoL
features in other languages. This fortunately gives it a lot of features other
languages don't have, and it unfortunately gives it a lot of features other
languages don't have.

Once again, the greater point being a warning, that C++ can easily become a
time sink in language-specific knowledge instead of domain-specific knowledge.
Of course sometimes that's what you want, e.g. when trying to optimize for
performance.

[1]:
[https://lobste.rs/s/tul188/initialization_c_is_seriously_bon...](https://lobste.rs/s/tul188/initialization_c_is_seriously_bonkers)

[2]:
[https://en.wikipedia.org/wiki/Wikipedia:Chesterton%27s_fence](https://en.wikipedia.org/wiki/Wikipedia:Chesterton%27s_fence)

~~~
gamma-male
Why not teach rust?

~~~
MikeLui
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++

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

~~~
MikeLui
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.

------
hellofunk
Not sure where it is but I saw a slide recently from a cpp conference that
showed something like 25 different ways to initialize an integer. I wasn't at
the conference but I wonder what point the speaker was making.

------
linuxlizard
C++ is a little like the English language: a bunch of different pieces
gathered together over a period of time, new features added to replace/fix
old. I like it. (My comment copy/paste from another C++ thread.)

------
jokoon
How many languages give you that much precise control over memory? So few.

~~~
int_19h
Many sibling languages in the era all of them originally appeared did so -
Pascal (not the core language, but real-world implementations with their
extensions, like Borland's), Modula-2 etc.

------
mlthoughts2018
It reminds me of this gem:
[https://mobile.twitter.com/timur_audio/status/10040173623817...](https://mobile.twitter.com/timur_audio/status/1004017362381795329)

------
amelius
Navigation on this page is also pretty bonkers, iyam.

~~~
shmageggy
Yeah, I have nav labels hanging over and obscuring the text. (FF on Ubuntu)

~~~
MikeLui
Yikes! That's no bueno. And away it goes...

