Skillversitas

Data Types and Variables in C++

A variable is a name for a memory location that stores data, while a data type determines the kind and size of data that can be stored in the variable. Understanding data types and variables is a primary foundation in C++ programming, because this language is a statically typed language — a data type must be declared before use.


Primitive Data Types

C++ provides several primitive (built-in) data types that are the basic building blocks:

Data Type Size (typical) Value Range Usage
bool 1 byte true or false True/false values
char 1 byte -128 to 127 or 0 to 255 Single character
unsigned char 1 byte 0 to 255 Unsigned character
short 2 byte -32,768 to 32,767 Small integer
unsigned short 2 byte 0 to 65,535 Small positive integer
int 4 byte -2,147,483,648 to 2,147,483,647 Standard integer
unsigned int 4 byte 0 to 4,294,967,295 Positive integer
long 4-8 byte At least the same as int Long integer
unsigned long 4-8 byte 0 to 4,294,967,295+ Positive long integer
long long 8 byte -9.2×10¹⁸ to 9.2×10¹⁸ Very long integer
float 4 byte ±3.4×10³⁸ (6-7 digit precision) Single-precision floating point
double 8 byte ±1.7×10³⁰⁸ (15-16 digit precision) Double-precision floating point
long double 8-16 byte Larger than double Extended-precision floating point
void 0 byte No value Function return type, generic pointer

Note: The actual size of a data type can vary between platforms. Use the sizeof operator to find the actual size on your system.


Code Example

#include <iostream>
#include <limits>
#include <string>

int main() {
    // Basic data types
    bool active = true;
    char letter = 'A';
    int age = 25;
    float height = 1.75f;
    double weight = 68.5;
    wchar_t wideCharacter = L'Ω';

    // Output data types and their sizes
    std::cout << "=== Data Type Sizes ===" << std::endl;
    std::cout << "bool:          " << sizeof(bool) << " byte" << std::endl;
    std::cout << "char:          " << sizeof(char) << " byte" << std::endl;
    std::cout << "short:         " << sizeof(short) << " byte" << std::endl;
    std::cout << "int:           " << sizeof(int) << " byte" << std::endl;
    std::cout << "long:          " << sizeof(long) << " byte" << std::endl;
    std::cout << "long long:     " << sizeof(long long) << " byte" << std::endl;
    std::cout << "float:         " << sizeof(float) << " byte" << std::endl;
    std::cout << "double:        " << sizeof(double) << " byte" << std::endl;
    std::cout << "long double:   " << sizeof(long double) << " byte" << std::endl;
    std::cout << "wchar_t:       " << sizeof(wchar_t) << " byte" << std::endl;

    // Value ranges with <limits>
    std::cout << "\n=== Value Ranges ===" << std::endl;
    std::cout << "int min: " << std::numeric_limits<int>::min() << std::endl;
    std::cout << "int max: " << std::numeric_limits<int>::max() << std::endl;
    std::cout << "float min: " << std::numeric_limits<float>::min() << std::endl;
    std::cout << "float max: " << std::numeric_limits<float>::max() << std::endl;

    // Declaration and initialization
    int x;              // Declaration only (indeterminate value)
    int y = 10;         // Copy initialization
    int z(20);          // Direct initialization
    int w{30};          // Brace initialization (C++11, recommended)
    auto autoType = 42; // Auto: type is deduced automatically

    std::cout << "\n=== Variable Values ===" << std::endl;
    std::cout << "x: " << x << std::endl;
    std::cout << "y: " << y << std::endl;
    std::cout << "z: " << z << std::endl;
    std::cout << "w: " << w << std::endl;
    std::cout << "autoType: " << autoType << std::endl;
    std::cout << "active: " << std::boolalpha << active << std::endl;
    std::cout << "letter: " << letter << std::endl;
    std::cout << "height: " << height << std::endl;

    return 0;
}

Code Explanation

1. bool

The boolean data type stores a value of true or false. Its size is 1 byte.

bool finished = false;
bool valid = (10 > 5);  // true
bool isEmpty = (text == "");  // true if text is empty

2. char

The char data type stores a single character in 1 byte.

char letter = 'A';
char digit = '7';
char newline = '\n';  // Character escape sequence

// ASCII value
int asciiValue = letter;  // 65
char fromAscii = 65;     // 'A'

Common escape characters:

Escape Description
\n Newline (new line)
\t Horizontal tab
\\ Backslash
\' Single quote
\" Double quote
\0 Null character
\r Carriage return
\b Backspace

3. int

The integer data type is the most commonly used type for storing numbers without a decimal point.

int positive = 42;
int negative = -17;
int zero = 0;

4. float and double

Floating-point types for decimal numbers. float uses single precision, while double uses double precision.

float pi_float = 3.14159f;    // 'f' suffix for float
double pi_double = 3.14159;   // The default floating-point type in C++ is double

Tip: Use double as the default for floating point unless you have a specific reason to use float (for example, saving memory for a large array).


Data Type Modifiers

Modifiers are used to change the range or size of a basic data type:

signed and unsigned

// signed (default for int) — can be positive and negative
int temperature = -15;

// unsigned — can only be positive (or zero)
unsigned int population = 1500000;

short and long

short int smallNumber = 32000;
long int bigNumber = 2000000000L;
long long int veryBigNumber = 9000000000000000000LL;

Modifier Combinations

unsigned short us;      // Unsigned short integer
unsigned long ul;       // Unsigned long integer
unsigned long long ull; // Unsigned long long integer

The sizeof Operator

The sizeof operator returns the size of a data type in bytes.

#include <iostream>

int main() {
    std::cout << "Data type sizes on your system:" << std::endl;
    std::cout << "char:      " << sizeof(char) << " byte" << std::endl;
    std::cout << "int:       " << sizeof(int) << " byte" << std::endl;
    std::cout << "float:     " << sizeof(float) << " byte" << std::endl;
    std::cout << "double:    " << sizeof(double) << " byte" << std::endl;
    std::cout << "bool:      " << sizeof(bool) << " byte" << std::endl;

    // sizeof on a variable
    int data = 42;
    std::cout << "data:      " << sizeof(data) << " byte" << std::endl;

    // sizeof on an array
    int arr[10];
    std::cout << "arr:       " << sizeof(arr) << " byte" << std::endl;
    std::cout << "element:   " << sizeof(arr[0]) << " byte" << std::endl;
    std::cout << "count:     " << sizeof(arr) / sizeof(arr[0]) << std::endl;

    return 0;
}

The auto Keyword (C++11)

The auto keyword allows the compiler to automatically deduce the data type from the given expression.

auto x = 42;          // int
auto y = 3.14;        // double
auto z = 'A';         // char
auto name = "John";   // const char*
auto valid = true;    // bool
auto number = 42LL;    // long long

// Very useful for long data types
#include <vector>
#include <map>

std::vector<int>::iterator it = vec.begin();
// Can be replaced with:
auto it = vec.begin();

std::map<std::string, int>::iterator iter = myMap.begin();
// Can be replaced with:
auto iter = myMap.begin();
// Auto with reference
int score = 10;
auto& ref = score;  // ref is int&

// Auto with pointer
int* ptr = &score;
auto ptr2 = ptr;     // ptr2 is int*

// Auto with const
const int constant = 5;
auto k = constant;    // k is int (const is removed)
auto& kr = constant;  // kr is const int& (const is preserved)

Note: auto cannot be used without initialization. The compiler needs an initial value to deduce the type.

auto x;              // ❌ Error: no initialization
auto y = 10;         // ✅ Correct

Data Type Limits (<limits>)

The <limits> header provides detailed information about the limits of each data type.

#include <iostream>
#include <limits>

int main() {
    std::cout << "=== Data Type Limits ===" << std::endl;

    std::cout << "char:" << std::endl;
    std::cout << "  min: " << (int)std::numeric_limits<char>::min() << std::endl;
    std::cout << "  max: " << (int)std::numeric_limits<char>::max() << std::endl;

    std::cout << "\nint:" << std::endl;
    std::cout << "  min: " << std::numeric_limits<int>::min() << std::endl;
    std::cout << "  max: " << std::numeric_limits<int>::max() << std::endl;

    std::cout << "\nunsigned int:" << std::endl;
    std::cout << "  min: " << std::numeric_limits<unsigned int>::min() << std::endl;
    std::cout << "  max: " << std::numeric_limits<unsigned int>::max() << std::endl;

    std::cout << "\nlong long:" << std::endl;
    std::cout << "  min: " << std::numeric_limits<long long>::min() << std::endl;
    std::cout << "  max: " << std::numeric_limits<long long>::max() << std::endl;

    std::cout << "\ndouble:" << std::endl;
    std::cout << "  min: " << std::numeric_limits<double>::min() << std::endl;
    std::cout << "  max: " << std::numeric_limits<double>::max() << std::endl;
    std::cout << "  epsilon: " << std::numeric_limits<double>::epsilon() << std::endl;

    return 0;
}

Variable Scope

Scope determines where in a program a variable can be accessed.

Local Scope

A variable declared inside a function or code block can only be accessed within that function or block.

void function() {
    int x = 10;  // Local variable — can only be accessed here
    std::cout << x << std::endl;
}

int main() {
    function();
    // std::cout << x;  // ❌ Error: x is not recognized here
    return 0;
}

Global Scope

A variable declared outside all functions can be accessed from anywhere in the file.

#include <iostream>

int global_var = 100;  // Global variable

void display() {
    std::cout << "From function: " << global_var << std::endl;
}

int main() {
    display();
    std::cout << "From main: " << global_var << std::endl;

    global_var = 200;  // Change the global value
    std::cout << "After change: " << global_var << std::endl;

    return 0;
}

Note: The use of global variables should be avoided as much as possible because it can make code difficult to maintain and lead to hard-to-track bugs.

Block Scope

A variable declared inside a { } block is only valid within that block.

#include <iostream>

int main() {
    int x = 10;

    if (x > 5) {
        int y = 20;  // Block variable — only valid inside the if
        std::cout << "x = " << x << ", y = " << y << std::endl;
    }

    std::cout << "x = " << x << std::endl;
    // std::cout << "y = " << y;  // ❌ Error: y is not recognized

    for (int i = 0; i < 5; i++) {
        int temp = i * 2;  // temp is only valid inside the loop
    }
    // std::cout << temp;  // ❌ Error: temp is not recognized

    return 0;
}

Shadowing (Variable Overriding)

When a local variable has the same name as a variable in a broader scope, the local variable "shadows" that variable within its own scope.

#include <iostream>

int x = 100;  // Global variable

int main() {
    std::cout << "Global: " << x << std::endl;  // 100

    int x = 50;  // Shadows the global variable
    std::cout << "Local: " << x << std::endl;   // 50

    if (true) {
        int x = 10;  // Shadows the local variable
        std::cout << "Block: " << x << std::endl; // 10
    }

    std::cout << "Local again: " << x << std::endl;  // 50
    return 0;
}

Variable Initialization

C++ supports several ways to initialize a variable:

int a = 10;        // Copy initialization (C-style)
int b(20);         // Direct initialization
int c{30};         // Brace/Uniform initialization (C++11, recommended)
int d = {40};      // Copy list initialization

// Why is brace initialization recommended?
int x{3.14};       // ❌ Error: narrowing conversion not allowed
int y = {3.14};    // ❌ Error: narrowing conversion not allowed
int z = 3.14;      // ⚠️ Compilation succeeds, but the data is truncated (3)

// Initialization with auto
auto a = 10;       // int
auto b = 10.0;     // double

Everyday Use Cases

Simple Program: Calculating an Employee's Salary

#include <iostream>
#include <string>

int main() {
    std::string employeeName;
    int workHours;
    double ratePerHour;
    double baseSalary;
    double taxPercent;
    double netSalary;

    // Input
    std::cout << "Employee name: ";
    std::getline(std::cin, employeeName);
    std::cout << "Work hours: ";
    std::cin >> workHours;
    std::cout << "Rate per hour ($): ";
    std::cin >> ratePerHour;
    std::cout << "Tax percentage: ";
    std::cin >> taxPercent;

    // Calculation
    baseSalary = workHours * ratePerHour;
    double tax = baseSalary * (taxPercent / 100.0);
    netSalary = baseSalary - tax;

    // Output
    std::cout << "\n=== Pay Slip ===" << std::endl;
    std::cout << "Name: " << employeeName << std::endl;
    std::cout << "Work hours: " << workHours << " hours" << std::endl;
    std::cout << "Rate/hour: $ " << ratePerHour << std::endl;
    std::cout << "Base salary: $ " << baseSalary << std::endl;
    std::cout << "Tax (" << taxPercent << "%): $ " << tax << std::endl;
    std::cout << "Net salary: $ " << netSalary << std::endl;

    return 0;
}

Conclusion

C++ provides various primitive data types (bool, char, int, float, double, void, wchar_t) with modifiers (signed, unsigned, short, long) to adjust size and value range. Variables must be declared with a data type before use, and the sizeof operator can be used to find the actual size on a particular system. The auto keyword makes declarations easier with automatic type inference. It is important to understand variable scope (local, global, block) and to avoid unintended shadowing. Use <limits> to see the value limits of each data type.