Skip to content

Variables_datatypes

Variables are the foundation of any program. They store data that your program can manipulate. In this chapter, you’ll learn about the different data types in C++, how to declare and use variables, and important concepts like type safety and conversion.

A variable is a named storage location in memory that holds a value. Think of it as a labeled box where you can store and retrieve data.

┌─────────────────────┐
│ Variable │
│ ┌───────────────┐ │
│ │ myValue │ │
│ │ 42 │ │
│ └───────────────┘ │
│ ↑ name │
│ ↑ value │
│ ↑ type │
└─────────────────────┘

In C++, you must declare a variable before using it:

// Basic declaration
int age;
double price;
char grade;
// Declaration with initialization
int count = 10;
std::string name = "John";
// Multiple declarations
int a, b, c;
int x = 1, y = 2, z = 3;

C++ provides several fundamental data types:

┌─────────────────────────────────────────────────────────────┐
│ C++ Data Types │
├─────────────────────────────────────────────────────────────┤
│ Primary Types │
│ ├── Integer → int, short, long, long long │
│ ├── Float → float, double, long double │
│ ├── Character → char, wchar_t, char16_t, char32_t │
│ ├── Boolean → bool │
│ └── Void → void │
├─────────────────────────────────────────────────────────────┤
│ Derived Types │
│ ├── Pointer → int* │
│ ├── Reference → int& │
│ ├── Array → int[] │
│ ├── Function → void func() │
│ └── Class → class, struct, union │
└─────────────────────────────────────────────────────────────┘

Integer types represent whole numbers.

TypeTypical SizeTypical Range
short2 bytes-32,768 to 32,767
int4 bytes-2,147,483,648 to 2,147,483,647
long4 or 8 bytesPlatform dependent
long long8 bytesVery large numbers
// Signed (can be negative) - default
int signedInt = -10;
// Unsigned (only positive)
unsigned int unsignedInt = 10;
unsigned short ushortVar = 100;
unsigned long ulongVar = 1000;
// Decimal (base 10)
int decimal = 42;
// Octal (base 8) - starts with 0
int octal = 052;
// Hexadecimal (base 16) - starts with 0x
int hex = 0x2A;
// Binary (C++14) - starts with 0b
int binary = 0b101010;
// With suffix
long long bigNum = 123456789LL; // long long suffix
unsigned int posNum = 42U; // unsigned suffix

Use sizeof to find the size of any type:

#include <iostream>
int main() {
std::cout << "Size of int: " << sizeof(int) << " bytes" << std::endl;
std::cout << "Size of long: " << sizeof(long) << " bytes" << std::endl;
std::cout << "Size of double: " << sizeof(double) << " bytes" << std::endl;
std::cout << "Size of char: " << sizeof(char) << " bytes" << std::endl;
return 0;
}

Floating-point types represent numbers with decimal parts.

TypePrecisionTypical Size
float~6-7 digits4 bytes
double~15 digits8 bytes
long double~18 digits16 bytes (often)
// Without suffix - double by default
double d1 = 3.14;
// Float suffix
float f1 = 3.14f;
float f2 = 3.14F;
// Long double suffix
long double ld1 = 3.14L;
// Scientific notation
double big = 1.5e10; // 1.5 × 10^10
double small = 1.5e-5; // 1.5 × 10^-5
#include <iostream>
#include <iomanip>
int main() {
float f = 3.14159265358979323846;
double d = 3.14159265358979323846;
long double ld = 3.14159265358979323846;
std::cout << std::setprecision(20);
std::cout << "float: " << f << std::endl;
std::cout << "double: " << d << std::endl;
std::cout << "long double: " << ld << std::endl;
return 0;
}

Character types store single characters.

TypeDescription
charAt least 8 bits (1 byte)
wchar_tWide character (platform dependent)
char16_t16-bit Unicode (C++11)
char32_t32-bit Unicode (C++11)
// Character literals (single quotes)
char c1 = 'A';
char c2 = '9';
char c3 = '$';
// Escape sequences
char newline = '\n';
char tab = '\t';
char backslash = '\\';
char quote = '\"';
// ASCII values
char a = 65; // 'A'
std::cout << a << std::endl; // Prints 'A'
// Wide characters (for Unicode)
wchar_t w = L'Ω';

The boolean type represents true or false values.

bool isActive = true;
bool isComplete = false;
// Boolean from comparisons
int x = 10;
bool isPositive = (x > 0); // true
bool isEven = (x % 2 == 0); // true
// Boolean in conditions
if (isActive) {
// Do something
}

In C++, any value can be converted to bool:

  • 0 converts to false
  • Non-zero values convert to true
bool b1 = 0; // false
bool b2 = 42; // true
bool b3 = -1; // true (non-zero)
bool b4 = nullptr; // false

The void type represents “no type” or “no value”.

// Function that returns nothing
void greet() {
std::cout << "Hello!" << std::endl;
}
// Function that takes no parameters
void process(void); // C-style, avoid in modern C++
// Generic pointer (avoid in modern C++)
void* ptr;

C++ provides modifiers to alter the meaning of base types:

Makes a variable read-only:

const int MAX_SIZE = 100;
int const MAX_VALUE = 50; // Also valid, less common
// MAX_SIZE = 200; // ERROR: Cannot modify const
// const with pointers
const int* p1; // Pointer to const int
int const* p2; // Same as above
int* const p3; // Const pointer to int
const int* const p4; // Const pointer to const int

Compile-time constant expression:

constexpr int SIZE = 100; // Computed at compile time
constexpr int SQUARED(int x) { // Compile-time function
return x * x;
}
int arr[SQUARED(5)]; // Array of size 25

Makes a variable have static storage duration:

void function() {
static int count = 0; // Initialized once, retains value
count++;
std::cout << count << std::endl;
}
int main() {
function(); // prints 1
function(); // prints 2
function(); // prints 3
}

Declares a variable defined elsewhere:

file1.cpp
int globalVar = 42;
// file2.cpp
extern int globalVar;
std::cout << globalVar << std::endl; // prints 42

Create alternative names for types using typedef (legacy) or using (modern):

// C++11 using (preferred)
using IntPtr = int*;
using Matrix = std::vector<std::vector<int>>;
using Callback = void(*)(int);
// Legacy typedef
typedef int* IntPtr;
typedef void (*Callback)(int);

C++11 introduced auto for automatic type deduction:

auto i = 42; // int
auto d = 3.14; // double
auto s = "hello"; // const char*
auto sum = 1 + 2.0; // double
// With iterators (very useful)
auto it = vec.begin(); // std::vector<int>::iterator
// Function return type
auto add(int a, int b) -> int {
return a + b;
}

C++ automatically converts between compatible types:

int i = 42;
double d = i; // int → double (implicit)
int j = d; // double → int (implicit, truncates!)
char c = 'A';
int x = c; // char → int (ASCII value 65)
double d = 3.99;
// C-style cast (avoid in C++)
int i = (int)d; // 3
// C++ casts (preferred)
int i1 = static_cast<int>(d); // 3
int i2 = reinterpret_cast<int>(&d); // pointer to int
// const cast (avoid)
const int ci = 42;
int* pi = const_cast<int*>(&ci);
// Integer promotions
char c = 'A';
int i = c; // char → int
// Float to double
float f = 3.14f;
double d = f; // float → double

C++ provides multiple ways to initialize variables:

int a = 10; // Copy initialization
int b(20); // Direct initialization
int c{30}; // Brace initialization (preferred)
int d = {}; // Zero initialization
int e{}; // Zero initialization (preferred in modern C++)
// Prevents narrowing conversions
int x1{10}; // OK
int x2{10.5}; // ERROR: narrowing conversion
int y1 = 10; // OK
int y2 = 10.5; // OK: compiles but truncates (dangerous!)
// Works with containers
std::vector<int> v{1, 2, 3}; // Initializes with 3 elements

Variables have different lifetimes depending on where they’re declared:

// Global variable
int globalVar = 100;
int main() {
// Local variable
int localVar = 10;
// Block scope
{
int blockVar = 20;
std::cout << globalVar << localVar << blockVar;
}
// blockVar is not accessible here
return 0;
}
// Bad
int count;
std::string name;
// Good
int count = 0;
std::string name;
// Bad
int x, y, z;
double d;
// Good
int numberOfStudents;
double averageScore;
// Bad
#define PI 3.14159 // Macro, no type checking
// Good
const double PI = 3.14159;
constexpr double PI = 3.14159; // If known at compile time
// Good: when type is obvious
auto it = container.begin();
auto result = calculate();
// Bad: when it reduces readability
auto x = someFunction(); // What type is x?
#include <iostream>
#include <string>
#include <vector>
int main() {
// Integer types
int age = 25;
unsigned int population = 330000000;
long long nationalDebt = 34000000000000LL;
// Floating-point types
double pi = 3.14159265358979;
float price = 19.99f;
// Character
char grade = 'A';
// Boolean
bool isStudent = true;
// String (std::string is a class, not primitive)
std::string name = "John Smith";
// Container
std::vector<int> scores{95, 87, 92, 88};
// Output
std::cout << "Name: " << name << std::endl;
std::cout << "Age: " << age << std::endl;
std::cout << "Grade: " << grade << std::endl;
std::cout << "Is Student: " << std::boolalpha << isStudent << std::endl;
std::cout << "Pi: " << pi << std::endl;
return 0;
}
  • Variables store data in named memory locations
  • C++ has fundamental types: integers, floating-point, characters, and booleans
  • Use appropriate types for your data (e.g., int for integers, double for decimals)
  • Always initialize variables before use
  • Prefer brace initialization {} to prevent narrowing conversions
  • Use const and constexpr for constants
  • Be aware of implicit type conversions and potential data loss
  • Choose meaningful variable names

Now let’s learn about operators in C++.

Next Chapter: 05_operators.md - Operators and Expressions