Variables and Data Types
A variable is a named location in memory that holds a value. C++ is statically typed — every variable has a fixed type determined at compile time, and the compiler enforces type rules before your program even runs.
Declaring Variables
#include <iostream>
using namespace std;
int main() {
// Declaration only
int age;
// Declaration with initialisation (preferred)
int score = 100;
double price = 19.99;
char grade = 'A';
bool isActive = true;
// C++11: uniform initialisation (brace syntax — prevents narrowing)
int count{42};
double ratio{0.75};
// Multiple variables of the same type
int x = 1, y = 2, z = 3;
cout << score << " " << price << " " << grade << " " << isActive << "\n";
// Output: 100 19.99 A 1
return 0;
}Note: Always initialise variables when you declare them. Uninitialized variables contain garbage values — a common source of undefined behaviour in C++.
Integer Types
#include <iostream>
#include <climits> // INT_MAX, LONG_MAX, etc.
using namespace std;
int main() {
// Signed integers (can hold negative values)
short s = 32767; // 2 bytes: -32,768 to 32,767
int i = 2147483647; // 4 bytes: ~-2.1B to 2.1B
long l = 2147483647L; // 4 or 8 bytes (platform-dependent)
long long ll = 9223372036854775807LL; // 8 bytes: ≈ ±9.2 × 10^18
// Unsigned integers (non-negative only, double the positive range)
unsigned int ui = 4294967295U; // 4 bytes: 0 to ~4.3B
unsigned long long ull = 18446744073709551615ULL;
// Fixed-width types (guaranteed sizes) — prefer these in cross-platform code
#include <cstdint>
int8_t a = 127; // Exactly 8 bits signed
uint8_t b = 255; // Exactly 8 bits unsigned
int32_t c = 2147483647;
uint64_t d = 18446744073709551615ULL;
cout << "INT_MAX = " << INT_MAX << "\n"; // 2147483647
cout << "sizeof(int) = " << sizeof(int) << " bytes\n"; // 4
return 0;
}Floating-Point Types
#include <iostream>
#include <iomanip>
using namespace std;
int main() {
float f = 3.14f; // 4 bytes, ~7 significant digits
double d = 3.14159265358; // 8 bytes, ~15 significant digits
long double ld = 3.14159265358979L; // 8-16 bytes (platform-dependent)
cout << fixed << setprecision(10);
cout << "float: " << f << "\n"; // 3.1400001049
cout << "double: " << d << "\n"; // 3.1415926536
cout << "long double: " << ld << "\n"; // 3.1415926536
// Floating-point literals
double sci = 1.5e10; // 1.5 × 10^10 = 15,000,000,000.0
double neg = 2.5e-3; // 2.5 × 10^-3 = 0.0025
// Floating-point comparison — NEVER use ==
double a = 0.1 + 0.2;
double b = 0.3;
// a == b is FALSE due to binary representation
double epsilon = 1e-9;
bool equal = (a - b < epsilon && b - a < epsilon); // Correct
cout << "0.1+0.2 == 0.3: " << equal << "\n"; // 1 (true)
return 0;
}Warning: Use double as your default floating-point type, not float. The float type has only 7 significant digits of precision — subtle errors creep in quickly. Reserve float for performance-critical code like graphics.
Character Type
#include <iostream>
#include <cctype> // toupper, tolower, isdigit, isalpha
using namespace std;
int main() {
char ch = 'A';
cout << ch << "\n"; // A
cout << (int)ch << "\n"; // 65 (ASCII value)
char next = ch + 1;
cout << next << "\n"; // B (arithmetic on chars)
// Character testing functions
cout << isalpha('Z') << "\n"; // 1 (non-zero = true)
cout << isdigit('5') << "\n"; // 1
cout << toupper('a') << "\n"; // 65 (ASCII of 'A')
// Escape characters
cout << 'A' << '\t' << 'B' << '\n'; // A B
cout << "He said \"hello\"\n"; // He said "hello"
// Wide characters (Unicode)
wchar_t wch = L'€';
return 0;
}Boolean Type
#include <iostream>
using namespace std;
int main() {
bool isOpen = true;
bool isEmpty = false;
cout << boolalpha; // Print "true"/"false" instead of 1/0
cout << isOpen << "\n"; // true
cout << isEmpty << "\n"; // false
// Any non-zero integer converts to true
bool fromInt = 42;
cout << fromInt << "\n"; // true
// Booleans in arithmetic
int count = true + true + false;
cout << count << "\n"; // 2
return 0;
}The auto Keyword
C++11 introduced auto — the compiler deduces the type from the initialiser:
#include <iostream>
#include <vector>
using namespace std;
int main() {
auto x = 42; // int
auto y = 3.14; // double
auto z = 'A'; // char
auto flag = true; // bool
auto name = "hello"; // const char* (NOT std::string!)
// Most useful with complex types
vector<int> v = {1, 2, 3, 4, 5};
for (auto it = v.begin(); it != v.end(); ++it) {
cout << *it << " ";
}
// 1 2 3 4 5
// auto by reference (avoids copying)
for (auto& elem : v) {
elem *= 2;
}
// v = {2, 4, 6, 8, 10}
return 0;
}Constants
#include <iostream>
using namespace std;
int main() {
// const — value cannot be changed after initialisation
const int MAX_SCORE = 100;
// MAX_SCORE = 101; // ERROR: cannot assign to const
// constexpr — evaluated at compile time (faster, can be used in array sizes)
constexpr double PI = 3.14159265358979;
constexpr int BUFFER_SIZE = 1024;
char buf[BUFFER_SIZE]; // OK — BUFFER_SIZE is a compile-time constant
// constexpr function (computed at compile time if arguments are constants)
constexpr int square(int n) { return n * n; }
constexpr int result = square(5); // 25, computed at compile time
cout << PI << "\n"; // 3.14159265358979
cout << result << "\n"; // 25
return 0;
}Type Casting
#include <iostream>
using namespace std;
int main() {
// Implicit (automatic) conversion — C++ widens types safely
int i = 42;
double d = i; // int → double: OK, no data loss
cout << d << "\n"; // 42.0
// Narrowing — data loss may occur (compiler may warn)
double pi = 3.14159;
int truncated = pi; // 3 (decimal part lost)
cout << truncated; // 3
// C-style cast (avoid in modern C++)
double ratio = (double)7 / 2; // 3.5
// C++ static_cast (preferred — explicit and checked at compile time)
double result = static_cast<double>(7) / 2;
cout << result << "\n"; // 3.5
int large = 1000;
char c = static_cast<char>(large); // Narrowing — intentional
cout << (int)c << "\n"; // Truncated to char range
return 0;
}| Cast type | When to use |
|---|---|
static_cast<T>() | Safe, checked at compile time — use for most conversions |
reinterpret_cast<T>() | Low-level bit reinterpretation — use with extreme caution |
const_cast<T>() | Remove const — almost always a design smell |
dynamic_cast<T>() | Safe downcast in class hierarchies — runtime checked |
Overflow and Undefined Behaviour
#include <iostream>
using namespace std;
int main() {
// Integer overflow — undefined behaviour for signed types!
int max = 2147483647;
int overflow = max + 1; // UB — could be anything
cout << overflow << "\n"; // -2147483648 on most platforms (but not guaranteed)
// Unsigned overflow is defined — it wraps around
unsigned int u = 4294967295U; // UINT_MAX
unsigned int wrapped = u + 1;
cout << wrapped << "\n"; // 0 (wraps around)
// Safe check before overflow
if (max > INT_MAX - 1) {
cout << "Would overflow!\n";
}
return 0;
}Practical Example: Unit Converter
#include <iostream>
#include <iomanip>
using namespace std;
int main() {
const double KM_PER_MILE = 1.60934;
const double KG_PER_POUND = 0.453592;
const double C_TO_F_SCALE = 9.0 / 5.0;
const double C_TO_F_OFFSET = 32.0;
cout << fixed << setprecision(2);
// Distance
double miles = 26.2188; // Marathon distance
double km = miles * KM_PER_MILE;
cout << miles << " miles = " << km << " km\n";
// 26.22 miles = 42.20 km
// Weight
double pounds = 165.0;
double kg = pounds * KG_PER_POUND;
cout << pounds << " lbs = " << kg << " kg\n";
// 165.00 lbs = 74.84 kg
// Temperature
double celsius = 100.0;
double fahrenheit = celsius * C_TO_F_SCALE + C_TO_F_OFFSET;
cout << celsius << "°C = " << fahrenheit << "°F\n";
// 100.00°C = 212.00°F
// Integer division trap
int total = 7, parts = 2;
cout << "7/2 (int) = " << total / parts << "\n"; // 3
cout << "7/2 (double) = " << static_cast<double>(total) / parts << "\n"; // 3.5
return 0;
}Summary
- C++ has several integer types:
short,int,long,long long— prefer fixed-width types (int32_t,uint64_t) for portable code - Use
double(notfloat) as your default floating-point type charstores a single character and is just a small integer (ASCII value)boolstorestrueorfalse; any non-zero integer istrueautolets the compiler deduce the type — most useful with iterators and complex typesconstprevents modification;constexprguarantees compile-time evaluation- Use
static_cast<T>()for explicit type conversions — never use C-style casts in new code - Signed integer overflow is undefined behaviour — check before operating near the limits
Next, we'll explore C++ operators — arithmetic, relational, logical, bitwise, and how operator precedence determines the order of evaluation.