C++ Part I · Module 1 · Why C++ Matters in Semiconductor Engineering
What C++ Is and What It Is Not
Most engineers arrive at C++ carrying one of two wrong models. The first is that C++ is C with classes bolted on — which explains why so much C++ in the wild is C written in a file that ends in .cpp. The second is that C++ is an enormous language nobody finishes learning, which is demoralising and also beside the point. This chapter replaces both. C++ is a language defined by a written standard, built on the same object and memory model as C, with abstraction mechanisms deliberately designed so that using them costs nothing at run time. That last claim is the one people repeat without evidence, so rather than asserting it we will compile the same computation two ways and count the instructions. You will write very little code here. You will finish knowing precisely what kind of language you are about to learn, and what it does not do for you.
Foundation14 min readC++Part IFundamentalsMental Model
Part I · Module 1 · Chapter 1.1 · What C++ Is and What It Is Not
1. C++ Is a Set of Rules, Not a Compiler
C++ is a programming language: an agreed set of rules for writing down what a program should do. You write it as ordinary text in a file that conventionally ends in .cpp, and that text is called source code.
Those rules are not defined by the compiler on your machine. They are defined by a written specification — an ISO standard document — and a compiler is a piece of software that implements that specification. GCC is not C++. Clang is not C++. MSVC is not C++. They are three implementations of the same document, which is exactly why the same source file can be handed to any of them.
This is not a pedantic distinction; it is load-bearing, and the rest of this curriculum depends on it. When a later chapter says "this is undefined behaviour", it means the standard declines to say what happens — not "my compiler did something odd". When a chapter says "the compiler may reorder this", it means the standard permits it, so a different compiler, or the same compiler at a different optimisation level, may do something else and still be correct. Chapter 1.3 makes that vocabulary precise.
The practical form of the rule is:
The language is the specification. Your compiler is one implementation of it, and your machine is one target of that implementation.
Everything you write against the specification travels. Everything you write against one compiler's habits does not.
2. The File You Write Is Not the Program That Runs
Your processor cannot read .cpp files. The text has to be translated into a form the machine can load and execute, and until that happens your source code is a description of a program rather than a program.
Two consequences follow immediately and are worth fixing in place now:
- Editing a source file changes nothing until it is translated again. A program that already exists is unaffected by edits to the text it came from.
- There are therefore two completely different times at which things can go wrong: during translation, and while the program runs. A C++ compiler rejecting your code and a C++ program crashing are not variations of the same event. They are different failures, found by different tools, fixed in different ways.
C++ leans on that first window far harder than most languages do. A great deal of what this curriculum teaches — types, const, overload resolution, templates, concepts — exists so that mistakes are caught during translation, when they cost you a message, rather than during execution, when they cost you a debugging session. Chapter 1.7 builds the full model of how source becomes a running program, and Module 2 opens up each stage.
3. Where C++ Sits
It helps to place C++ against the things it is routinely confused with. Each row below is a real difference in kind, not a matter of taste.
| What it is | Who decides the machine code | Runtime support needed | |
|---|---|---|---|
| Machine code | Numbers a specific processor executes directly | You, implicitly | None |
| Assembly | Text naming individual operations for one processor family | You, one operation at a time | None |
| C | A standard-defined language describing required behaviour | The compiler | A small library; no managed runtime |
| C++ | A standard-defined language with C's memory model plus abstraction mechanisms | The compiler | A larger library; still no managed runtime, no garbage collector |
| Python / Java-style | A language executed by an interpreter or virtual machine | The runtime, while the program runs | A substantial managed runtime |
Two readings of that table matter.
C++ is on the same side of the line as C. There is no interpreter between your program and the machine, no garbage collector deciding when memory is released, no managed runtime standing between your code and the hardware. This is why C++ is used where it is used — which Chapter 1.5 covers in detail — and it is also why C++ hands you responsibilities a managed language would take.
C++ is not assembly, and "portable assembly" is a phrase to retire. The standard constrains what your program must appear to do, not the steps taken to achieve it. There is no fixed correspondence between a line of C++ and a sequence of machine instructions, and §6 of this chapter will show that correspondence failing by a factor of three.
4. Multi-Paradigm, Precisely
C++ is routinely called "multi-paradigm". That phrase usually arrives as a marketing adjective; it has a concrete meaning, which is this: C++ supports several distinct ways of organising a program, none of which it forces on you, and good C++ mixes them deliberately.
- Procedural. Functions operating on data, exactly as in C. A standalone function that transforms a buffer is idiomatic C++, not a failure to be object-oriented.
- Object-based. Types that bundle data with the operations allowed on it, and — more importantly — types that can guarantee something about their own data. Modules 11 and 12 develop this properly.
- Object-oriented. Runtime substitutability through inheritance and virtual functions, so one piece of code drives many implementations chosen while the program runs (Modules 16 and 17).
- Generic. Code written against the operations a type must support rather than against a named type, resolved at compile time (Module 18). Every standard container and algorithm is built this way.
- Functional in style. Small callable objects passed to algorithms, values transformed rather than mutated in place (Module 20).
The important engineering consequence is that there is no single "C++ way" to write a program, and choosing badly between these is one of the most common ways C++ code goes wrong. A class hierarchy where a plain function would do is a cost with no benefit. A template where a concrete type would do makes error messages worse for nothing. Much of this curriculum is about making that choice on evidence.
What C++ is not is a language where object orientation is mandatory. Nothing in C++ requires you to put code inside a class. That misreading is the direct cause of a style of C++ that is harder to read than the C it replaced.
5. Built on C's Object and Memory Model
This is the part that makes C++ feel familiar to a C programmer and the part that makes it dangerous to someone arriving from a managed language.
In both C and C++, an object is a region of storage with a type. It lives at an address. It comes into existence at some point, stops existing at another, and reading it outside that window is undefined. Nothing watches over it for you. Nothing reclaims it automatically. The rules about representation, alignment and lifetime that govern a C struct of 32-bit integers govern the C++ equivalent too.
That shared foundation is the reason C++ shows up in the places Chapter 1.5 describes: anywhere the layout of data in memory is part of the problem, anywhere a result must be produced within a fixed time budget, anywhere the program has to interoperate with code written in C. A language that hides memory cannot do those jobs.
It is also the reason C++ has an undefined-behaviour problem at all, and why Module 25 exists. The abstractions you will learn in Modules 12 through 14 are not decoration on top of that model; they are disciplined ways of managing it, which is a different and much more interesting claim. Chapter 1.4 inventories exactly what carries over from C and what does not.
6. Abstraction That Leaves Nothing Behind — Measured
Here is the claim people repeat: C++ abstractions are "zero-cost", or "zero-overhead". Here is what it actually means, and here is the evidence.
Consider summing a sequence of 32-bit values — the kind of thing a checksum, a coverage tally or a packet-length total needs. Written as an index loop:
#include <cstdint>
#include <vector>
std::uint32_t total(const std::vector<std::uint32_t>& values)
{
std::uint32_t sum = 0;
for (std::size_t i = 0; i < values.size(); ++i) {
sum += values[i];
}
return sum;
}And written with a standard algorithm — the same computation, expressed as intent rather than as iteration:
#include <cstdint>
#include <numeric>
#include <vector>
std::uint32_t total(const std::vector<std::uint32_t>& values)
{
return std::accumulate(values.begin(), values.end(), std::uint32_t{0});
}You are not expected to understand the second one yet. std::vector is Module 19, iterators and std::accumulate are Module 20, and the std::uint32_t{0} initialisation form is Module 3. It is here for one reason: the second version goes through several layers of abstraction — a container, two iterators, a function template instantiated for those iterator types — and the first goes through almost none. If abstraction costs something at run time, the difference should be visible.
Compile both and count the machine instructions the compiler produced. On the machine used to write this chapter — Apple clang 21 on arm64, -std=c++17 — the result splits sharply by optimisation level:
instructions function calls
-O0 index loop 50 2
-O0 std::accumulate 164 15
-O2 index loop 61 0
-O2 std::accumulate 64 0Read those two blocks as two different facts.
At -O0, the abstraction is unmistakably there. 164 instructions against 50, and fifteen function calls where the index loop makes two. Every one of those calls is real: constructing iterators, comparing them, dereferencing them, advancing them. Anyone who tells you the abstraction "doesn't exist" is wrong — in the language's own semantics it absolutely exists, and in a debug build you can count it.
At -O2, it is gone. Both versions contain zero function calls. The iterator layer, the container's accessors, the algorithm template — all of it was inlined away until nothing was left but the loop. The remaining difference is three instructions of loop setup and a different choice of registers. Both versions ended up using five SIMD vector registers, which means the compiler auto-vectorised both: it is processing several values per iteration in each case.
So the precise claim is:
"Zero-cost abstraction" means the abstraction is a compile-time construct that the optimiser can see through completely, leaving no runtime machinery — no dispatch, no allocation, no indirection — behind. It does not mean the machinery was never there, and it does not mean identical output.
Two honest limits on the claim, because an over-strong version of it is its own problem:
- "When used as intended" is doing real work in that sentence. Some C++ abstractions genuinely do cost at run time, by design — a virtual function call is an indirection the compiler usually cannot remove (Module 17.7), and
std::shared_ptrmaintains an atomic reference count (Module 14.5). Those are features with a runtime price, chosen when the price buys something. Knowing which abstractions are free and which are not is a skill this curriculum builds deliberately. - Zero-cost is a claim about the abstraction, not about your algorithm. No amount of inlining rescues an O(n²) approach. The compiler removes the cost of expressing the computation well; it does not choose a better computation.
7. A Preview of the Shape, Not the Syntax
One more piece of C++ to look at, so that the rest of this module has something concrete to point at. Nothing here is being taught yet.
struct RegisterCheck {
std::uint32_t address;
std::uint32_t expected;
std::uint32_t actual;
bool passed() const { return expected == actual; }
};In ordinary English: a register check is an address together with the value we expected and the value we observed, and it passed when those two values are equal.
Three things are worth noticing, each of which is a thread picked up later in the curriculum:
- The data and the question you ask about it live together.
passed()is not a free-floating utility that callers must remember to apply correctly; it is part of what aRegisterCheckis. Module 11 develops this as encapsulation. constonpassed()is a promise checked by the compiler — this operation does not modify the object. That promise is enforced during translation, not hoped for in review. Module 11.7.- The fixed-width types are deliberate.
std::uint32_tsays exactly 32 bits, unsigned, which is the right thing to say about a value that came from a 32-bit register. Module 3.4 makes this a rule.
This little type will reappear through the module, because a running example that accumulates meaning teaches better than a fresh toy problem every chapter.
8. What C++ Does Not Do for You
A language is also defined by what it declines to do. If you are coming from a managed language, this list is the adjustment; if you are coming from C, most of it will read as unsurprising.
| C++ does | C++ does not |
|---|---|
| Check types during translation, strictly | Check array indices at run time |
| Run destructors deterministically, at a point you can name | Garbage-collect; nothing reclaims memory on its own |
| Let you define what copying your type means | Prevent you from copying something that should not be copied, unless you say so |
| Give you tools to make ownership explicit | Track ownership for you, or detect a leak |
| Detect many mistakes before the program runs | Detect a dangling reference, a data race, or arithmetic overflow at run time |
| Permit abstraction with no runtime cost | Make a poorly chosen abstraction cheap |
The entry worth lingering on is the second row, because it is the one that is genuinely better in C++ than in a managed language rather than merely different. C++ releases a resource at a determined point — when the owning object's lifetime ends — rather than at some unspecified later time chosen by a collector. That determinism is why C++ can hold things that are not memory at all: a file, a lock, a handle into a simulator. Modules 12 and 14 turn this into the language's central idiom. Chapter 1.2 explains why it was designed that way.
The rest of the table is the honest cost of sitting on the same side of the line as C. Nothing in C++ will stop you indexing past the end of an array; Module 25 is about the tools that find it when you do.
9. The Common Misreadings
Three wrong models, and what each one costs the person holding it.
"C++ is C with classes." This describes C++ as it existed around 1983 and is now actively misleading. A C programmer who writes C in .cpp files — manual malloc/free, raw pointers everywhere, no value semantics, no const discipline — gets all of C's hazards plus a slower compiler and none of the benefits. Chapter 1.4 is entirely about this boundary, because getting it wrong is the single most common way an experienced C engineer writes bad C++.
"C++ is huge, so I need to learn all of it." You do not. There is a coherent subset that covers the overwhelming majority of real engineering work, and this curriculum teaches that subset in dependency order. The parts of C++ that exist for library authors and standards committees are not prerequisites for writing a correct reference model.
"C++ is fast." Not by itself. C++ allows code to be fast by not inserting machinery you did not ask for — that is the real claim, and §6 is what it looks like. It does not make a wasteful program efficient, and a C++ program that copies a large container at every function boundary is slower than the equivalent C. Module 25.9 is about exactly those hidden copies.
10. What You Should Be Able to Say Now
- C++ is defined by a written ISO standard. A compiler is an implementation of that standard, and your machine is one target of that implementation.
- Source code is text describing a program; a program is what runs. Compile time and run time are different windows in which different things go wrong.
- C++ sits on the same side of the line as C: no interpreter, no virtual machine, no garbage collector.
- "Multi-paradigm" means C++ supports procedural, object-based, object-oriented, generic and functional styles, and forces none of them. Choosing badly between them is a real and common failure.
- An object in C++ means typed storage, not "instance of a class". Objects have addresses and lifetimes, and nothing manages them for you.
- C++ shares C's object and memory model, which is why it is used for hardware-adjacent work and why undefined behaviour is a real concern.
- Zero-cost abstraction means the abstraction is a compile-time construct the optimiser removes entirely — measured here as 15 function calls at
-O0and 0 at-O2. It does not mean the machinery was never there. - Some abstractions do cost at run time by design — virtual dispatch, reference counting — and knowing which is part of the skill.
- C++ checks a great deal during translation and almost nothing during execution. No bounds checks, no leak detection, no race detection.
- Resource release in C++ is deterministic. That is a design decision, not an omission, and it is what makes the language able to manage things other than memory.
11. Exercises
Exercise 1 — classify the failure. For each, say whether it is a compile-time or a run-time failure, and give a one-line reason: (a) passing a double where a function requires a std::vector; (b) reading element 10 of a 4-element vector; (c) misspelling a type's name; (d) a program that produces the wrong checksum.
Exercise 2 — argue against the phrase. A colleague describes C++ as "portable assembly". Using §3 and §6, give two distinct reasons the description is wrong. One of your reasons should refer to a number from the measurement.
Exercise 3 — state the claim precisely. Write one sentence defining "zero-cost abstraction" that would survive a reading of the -O0 column. Your sentence must not contain the words "no cost" without qualification.
Exercise 4 — the word "object". A reviewer writes: "this function takes three objects and one int". Explain why that sentence is confused in C++ terms, and rewrite it.
Exercise 5 — the deliberate omissions. Pick two rows from §8's right-hand column. For each, describe a specific bug it permits, and name the kind of tool or discipline an engineer uses instead of a language guarantee.
Exercise 6 — predict the measurement. Suppose the two functions in §6 were compared at -O1 instead of -O0 and -O2. Say what you would expect of the call count and why — and say what you would do to find out rather than guess.
12. Summary
- C++ is a specification, not a compiler. GCC, Clang and MSVC are implementations; portable code is written against the document, not against one implementation's habits.
- Source is not program; compile time is not run time. C++ invests heavily in catching mistakes in the first window, which is why so much of the language is about types.
- C++ shares C's object and memory model. Objects are typed storage with addresses and lifetimes, nothing supervises them, and "object" does not mean "class instance".
- Multi-paradigm means a real choice. Procedural, object-based, object-oriented, generic and functional styles all belong; choosing the wrong one is a cost with no benefit.
- Zero-cost abstraction is measurable, and it was measured. The same sum written through a container and an algorithm produced 15 function calls at
-O0and none at-O2. The abstraction is a compile-time construct the optimiser erases. - Not every abstraction is free. Virtual dispatch and reference counting cost at run time by design. Knowing which is which is engineering judgement, taught module by module.
- C++ declines to protect you at run time. No bounds checks, no collector, no leak or race detection — in exchange for determinism and control.
The next question is the obvious one. If C++ sits beside C, shares its memory model, and adds abstractions that mostly compile away, what was the actual problem that justified building it? C++ was not designed as an improvement in the abstract; it was designed against a specific difficulty that large systems programs ran into and that C had no answer for. Chapter 1.2 is that problem, and knowing it is what makes the rest of the language read as a set of decisions rather than a pile of features.
Standards & specifications
- Governing standard
- ISO/IEC 14882 (the C++ language standard)(opens ISO/IEC (JTC 1/SC 22/WG 21) in a new tab)
Defines the C++ language and its standard library — the type system and templates, the object model and object lifetime, initialisation and overload resolution, the memory model and the containers, algorithms and utilities the library provides. Like C's standard it is a FAMILY of editions (C++98/03, C++11, C++14, C++17, C++20, C++23) and the editions are NOT interchangeable: a feature this curriculum relies on may be unavailable, differently specified or merely proposed in another edition, so each lesson names the edition its claim depends on. It does not define any foreign-function interface to a hardware description language, nor what a compiler actually emits, nor platform behaviour on real silicon — those are the implementation's and the device's contracts. Working drafts published by WG21 are freely available and are what most engineers read day to day; a draft is not the published standard.
This page also covers RTL structure, verification approach and debugging technique. Those are engineering practice built on the standard, not requirements the standard itself imposes.
Where this fits
Part of the C++ Programming curriculum.
