Course outline · 0% complete

0/27 lessons0%

Course overview →

The Core Types and const

lesson 2-1 · ~12 min · 4/27

Types have sizes

Lesson 1-3 ended with a fix to int name = "Grace";. The original line failed because an int variable can only ever hold whole numbers, and the compiler enforces that before the program runs.

That is static typing in one sentence: a variable's declared type is fixed, every assignment is checked against it at compile time, and text simply cannot be stored in an int, so no executable is produced.

This lesson goes one level deeper into what a type actually is, because in C++ a type carries a size in bytes as well as a kind of value, and that size has consequences you can measure.

The five types you will use constantly

Every C++ type has a fixed size in memory, and that is the deal that makes the language fast. The compiler knows at build time exactly how many bytes each variable needs, so it can lay out memory and emit raw CPU instructions with no runtime type checks at all.

It also means choosing a type is a real engineering decision. In 1996 the Ariane 5 rocket was destroyed seconds after launch, partly because a value was converted into an integer type too small to hold it.

TypeHoldsExample
intwhole numbers (about ±2.1 billion)int n = 42;
doubledecimal numbers (64-bit floating point)double t = 9.81;
booltrue or falsebool ok = true;
charone single character, in 'single quotes'char grade = 'A';
std::stringtext of any length, in "double quotes"std::string s = "hi";

Four notes for Python programmers:

  • Python's int grows without limit. A C++ int is a fixed 32-bit box, so any value that can exceed roughly 2.1 billion, which is common in interview math, belongs in a long long instead.
  • float also exists but is less precise than double, so default to double.
  • char and std::string are genuinely different types. 'A' is one character and "A" is a string that happens to contain one character.
  • std::string needs #include <string>.
bool char int double long long 1 byte 1 byte 4 bytes 8 bytes 8 bytes max ~2.1e9 max ~9.2e18
Each type occupies a fixed number of bytes, which is what lets the compiler lay out memory ahead of time. An int is a 32-bit box, so a long long is needed once values pass about two billion.

A tour of the five types

One variable of each type, printed together, plus a demonstration of how bool values reach the output stream.

#include <iostream>
#include <string>

int main() {
    int n = 42;
    double t = 9.81;
    bool ok = true;
    char grade = 'A';
    std::string lang = "C++";

    std::cout << n << " " << t << " " << grade << " " << lang << "\n";
    std::cout << ok << "\n";                    // bools print as 1 or 0
    std::cout << std::boolalpha << ok << "\n";  // now they print as words
    return 0;
}

Output

42 9.81 A C++
1
true

The bool is the one that surprises people. By default a stream prints true as 1 and false as 0, because that is the underlying numeric value. Sending std::boolalpha into the stream switches it to printing the words instead, and that setting sticks for every later bool on the same stream rather than applying only to the next one.

Notice also that grade prints as the character A rather than as a number, even though a char is stored as a small integer. The stream chooses its formatting from the type, which is why char and int print so differently despite their similar storage.

When int is not enough

The sum 1 + 2 + ... + n equals n(n + 1)/2. For n = 100,000 that is 5,000,050,000, which is well beyond an int's ceiling of roughly 2.1 billion.

Computing it in int arithmetic does not produce a slightly wrong answer, it produces a meaningless one. The true value cannot fit in the 32-bit box, and signed overflow is undefined behavior, meaning the compiler is allowed to assume it never happens and optimize accordingly. Declaring the variables long long makes every step of the arithmetic 64-bit:

long long n = 100000;
long long sum = n * (n + 1) / 2;   // 5000050000, fits comfortably

The rule interviewers expect you to know is to estimate the largest possible value before summing or multiplying. Anything that can pass roughly 2 × 10⁹ belongs in a long long.

The same sum in 64-bit arithmetic

The formula from above, computed with long long variables so that no intermediate step overflows.

#include <iostream>

int main() {
    long long n = 100000;
    long long sum = n * (n + 1) / 2;
    std::cout << sum << "\n";
    return 0;
}

Output

5000050000

The order of operations inside that expression is what makes the type choice matter so much. n * (n + 1) is computed first, giving 10,000,100,000, and only then is the result halved. If both variables were plain int, the multiplication would overflow before the division ever got a chance to bring the value back into range.

That detail generalizes well. It is not enough for the final answer to fit in the type, because every intermediate value has to fit too, and multiplication is where the intermediate values grow fastest.

const: values that never change

Mark a variable const and the compiler forbids every later assignment to it:

const double PI = 3.14159;
PI = 3.2;   // COMPILE ERROR: assignment of read-only variable

Python has the ALL_CAPS naming convention for constants, but nothing stops you from reassigning them. In C++, const is enforced by the compiler, just like the type checking you met in lesson 1-3.

Use const on everything that should not change. It documents intent, prevents accidental writes, and later in the course it becomes essential when passing big objects to functions cheaply and safely.

A const value used in a calculation

PI is a value the program should never change, so it is declared const, while radius arrives from input and is an ordinary double.

#include <iostream>

int main() {
    const double PI = 3.14159;
    double radius;
    std::cin >> radius;
    std::cout << "Area: " << PI * radius * radius << "\n";
    return 0;
}

Input

2.0

Output

Area: 12.5664

Multiplication is spelled * and there is no exponent operator in C++, so squaring the radius is written out as radius * radius. That is also the fastest form, since std::pow(radius, 2) from <cmath> does considerably more work for the same result.

The printed value is 12.5664 rather than the full 12.566360, because std::cout shows doubles with six significant digits by default and rounds the last one. Controlling that precision is possible with std::setprecision from <iomanip>, and lesson 10-1 uses it when exact output formats matter.