CS 70

C++ Guiding Principles

Many of the seemingly strange behaviors of C++ can be understood if we put them into the context of the design principles that underlay the language.

It's important to remember that programming languages are designed and written by people, and that different languages behave differently depending on the goals of those people. C++ was developed with very different goals than Java:

  • Java's original design goals: One of the primary goals of the team that developed Java was security: they were developing the language for smart appliances, and wanted to make sure that developer errors couldn't lead to users being able to do things they shouldn't.
  • C++'s original design goals: On the other hand, C++ was developed by Bjarne Stroustrup, a Danish computer scientist who wanted to be able to write code that would analyze the performance of the UNIX operating system. Because of his context, Stroustrup wanted a language that was fast and that would give him a lot of control.

C++'s original design goals led to two guiding principles that we will revisit over and over again this semester as we examine the decisions made by Stroustrup about how C++ behaves.

  • Bjarne speaking

    C++ has evolved a lot in the decades since I designed it, but those original design decisions I made remain a huge influence on the shape of the language.

The Zero-Overhead Principle

protest sign with crossed-out word overhead

When a developer writes a new programming language, they want people to use it! Stroustrup was writing the C++ language as an extension of the C programming language. To ensure he had a community of programmers who would be interested in his new language, one promise he made was that anything that could be done in C would be possible in C++, and that developers wouldn't pay for any of the new C++ features if they didn't use them. So a C++ program should be as fast (and should use as little memory) as the equivalent C program. It also means that some behaviors of C++ are inherited from C.

What the Zero Overhead Principle means for us is that, in many cases, we'll be able to reason about the most likely behavior of C++ by asking ourselves what would be the fastest, most-efficient behavior it could display. Sometimes that behavior is different on different systems. We've seen an example with uninitialized variables being given indeterminate values.

Similarly, whereas Java gives an error if you try to access beyond the end of an array, C++ just says “you must not do that”. By putting the burden on you not to make that mistake, it means your program doesn't need to “waste time” checking that all your array accesses are in bounds.

  • Hedgehog speaking

    But what if I screw up and access beyond the end of the array?

  • Bjarne speaking

    You broke the rules!

C++ makes no promises at all about what will happen if you break that rule. This lack of any promise about what might happen is known as undefined behavior. Perhaps your program will immediately crash, or maybe it'll crash strangely sometime later; maybe it will just produce the wrong answers; maybe it will still work as you wanted (as far as you can tell).

  • Goat speaking

    Meh. And maybe it will let hackers take over your machine…

  • LHS Cow speaking

    Most security vulnerabilties in today's software are due to C and C++'s lack of checking when accessing arrays!

C++ could have been designed to avoid undefined behavior and check things more thoroughly, but that would be slower (or sometimes really hard to figure out how to do), so most C++ implementations don't.

  • Duck speaking

    But if my program compiles and runs fine, it's all good right?

  • LHS Cow speaking

    Actually, no. Just because your program seems to work doesn't mean it's actually correct. If your code causes undefined behavior, it may seem to work now but go disastrously wrong tomorrow (or on the next run, or on someone else's machine, or…).

  • RHS Cow speaking

    So avoid undefined behavior in your C++ code! If your code gives compiler warnings, you should fix them—even if your code seems like it works! Programs that rely on undefined behavior are flawed, and bugs that are a result of undefined behavior can be particularly tough to track down.

The “Power to the People” Principle

protest signs reading power to the people and the cows

C programmers were used to having a lot of control over what their program did, and didn't want to give that up. And Stroustrup's goal of being able to analyze the behavior of the UNIX kernel also required a lot of control over the behavior of the programs he wrote.

The Power to the People Principle means the C++ language often gives programmers an unusual level of control, and trust. In the hands of a skilled programmer, this additional power can be a useful tool allowing them to apply the language in unusual ways.

But, for most programmers, especially beginners, this added power is actually unhelpful. Most of the time, when someone tries to do something in bizarre and unusual way, it's not because they're an expert cleverly bypassing the usual rules, its just a mistake.

Thankfully, compiler warnings, and the cpplint tool we use in CS 70, both help catch many instances of doing things improperly. But it is good to remember that just because a particular program compiles does not mean that it is using C++ features sensibly.

  • Duck speaking

    So C++ is like the wild west with no rules?

  • LHS Cow speaking

    No. There are plenty of rules. Many kinds of errors are caught when we compile our programs.

  • RHS Cow speaking

    And compiler warnings are also huge red flags.

  • LHS Cow speaking

    But sometimes inexperienced C++ programmers stumble onto language features designed for experts and inadvertently apply them (wrongly). That's why we need to warn you.

Again, in this question, don't worry if you are right or wrong. Just think it through, give your best answer, and learn from the feedback!

Question

Based on what we know about the motivations of C++, which of the following would you predict will happen if we don't tell the compiler what the initial value of a variable should be?

(When logged in, completion status appears here.)