C++ is statically typed. Fundamental types, auto type deduction, arithmetic/logical/bitwise operators, and the four named casts — static_cast, const_cast, dynamic_cast, reinterpret_cast.
Every variable has a fixed type known at compile time — the compiler checks operations against it. Use fixed-width integer types from <cstdint> when the exact size matters. const marks a value that never changes.
#include <cstdint>
int main() {
int count = 42; // usually 32-bit signed
double price = 19.99; // 64-bit floating point
char grade = 'A'; // a single byte / character
bool active = true; // true or false
std::int64_t big = 9'000'000'000; // exact-width, digit separators
const double kPi = 3.14159; // cannot be reassigned
unsigned int mask = 0xFF; // no sign bit — wraps around at 0
return 0;
}auto tells the compiler to infer the type from the initializer. It is not dynamic typing: the type is still fixed at compile time. It shines with long template types like iterators, where spelling the type out is noisy and error-prone.
#include <string>
#include <vector>
int main() {
auto n = 10; // int
auto ratio = 3.0 / 4; // double
auto name = std::string{"Ada"}; // std::string
std::vector<int> v{1, 2, 3};
for (auto it = v.begin(); it != v.end(); ++it) {
// 'it' is a std::vector<int>::iterator — auto saved you typing it
}
return 0;
}Arithmetic works as expected, but watch integer division: 7 / 2 is 3, not 3.5. Logical operators (&&, ||, !) short-circuit. Bitwise operators (&, |, ^, ~, <<, >>) work on the individual bits — the backbone of flags, masks, and low-level formats.
#include <iostream>
int main() {
int a = 7, b = 2;
std::cout << a / b << " " << a % b << "\n"; // 3 1 (integer division)
std::cout << 7.0 / 2 << "\n"; // 3.5 (one double promotes)
bool ok = (a > 0) && (b != 0); // short-circuits
unsigned flags = 0;
flags |= (1u << 2); // set bit 2
bool set = flags & (1u << 2); // test bit 2
flags &= ~(1u << 2); // clear bit 2
std::cout << ok << " " << set << "\n"; // 1 1
return 0;
}C++ replaces the blunt C cast with four explicit ones, each stating intent. static_cast is the everyday, checked-at-compile-time conversion. const_cast adds or removes const. dynamic_cast safely down-casts polymorphic pointers at runtime. reinterpret_cast reinterprets raw bits — powerful and dangerous, used rarely.
int main() {
double d = 3.99;
int i = static_cast<int>(d); // 3 — deliberate narrowing
const int c = 10;
int& r = const_cast<int&>(c); // strip const (only safe if the
// underlying object isn't truly const)
long addr = 0x1000;
auto* p = reinterpret_cast<char*>(addr); // treat a number as a pointer
// (systems/hardware code only)
(void)i; (void)r; (void)p;
return 0;
}