Fundamentals

Operators

Master C++ operators — arithmetic, relational, logical, bitwise, assignment, ternary — and understand operator precedence and associativity.

Operators

Operators are symbols that perform operations on operands. C++ has a rich set of operators inherited from C plus additional ones for OOP. Understanding operator precedence prevents subtle bugs where expressions evaluate in an unexpected order.


Arithmetic Operators

cpp
#include <iostream>
using namespace std;

int main() {
    int a = 17, b = 5;

    cout << a + b  << "\n";   // 22   Addition
    cout << a - b  << "\n";   // 12   Subtraction
    cout << a * b  << "\n";   // 85   Multiplication
    cout << a / b  << "\n";   // 3    Integer division (truncates)
    cout << a % b  << "\n";   // 2    Modulo (remainder)

    double x = 17.0, y = 5.0;
    cout << x / y  << "\n";   // 3.4  Floating-point division

    // Unary operators
    int n = 10;
    cout << -n << "\n";       // -10  Unary negation
    cout << +n << "\n";       // 10   Unary plus (no-op)

    return 0;
}

Integer Division vs Floating-Point Division

cpp
cout << 7 / 2    << "\n";   // 3    (integer division — truncates towards zero)
cout << 7.0 / 2  << "\n";   // 3.5  (floating-point division)
cout << 7 / 2.0  << "\n";   // 3.5
cout << static_cast<double>(7) / 2 << "\n";  // 3.5 (explicit cast)

Increment and Decrement

cpp
#include <iostream>
using namespace std;

int main() {
    int x = 5;

    // Pre-increment: increment FIRST, then use the value
    cout << ++x << "\n";   // 6  (x is now 6)
    cout << x   << "\n";   // 6

    // Post-increment: use the value FIRST, then increment
    cout << x++ << "\n";   // 6  (prints old value, then x becomes 7)
    cout << x   << "\n";   // 7

    // Decrement works the same way
    cout << --x << "\n";   // 6
    cout << x-- << "\n";   // 6  (x becomes 5 after)

    return 0;
}

Note: Prefer ++i over i++ in loops — pre-increment is never slower and sometimes faster (especially with iterators).


Relational Operators

Relational operators compare two values and return true or false.

cpp
int a = 10, b = 20;

cout << (a == b) << "\n";  // 0 (false)
cout << (a != b) << "\n";  // 1 (true)
cout << (a <  b) << "\n";  // 1 (true)
cout << (a >  b) << "\n";  // 0 (false)
cout << (a <= b) << "\n";  // 1 (true)
cout << (a >= b) << "\n";  // 0 (false)

Logical Operators

Logical operators combine boolean expressions:

cpp
#include <iostream>
using namespace std;

int main() {
    bool sunny = true, warm = false, weekend = true;

    // AND — both must be true
    cout << (sunny && warm)   << "\n";  // 0 (false)

    // OR — at least one must be true
    cout << (sunny || warm)   << "\n";  // 1 (true)

    // NOT — inverts
    cout << (!sunny)          << "\n";  // 0 (false)
    cout << (!warm)           << "\n";  // 1 (true)

    // Combining
    bool goOutside = (sunny || warm) && weekend;
    cout << goOutside << "\n";  // 1 (true)

    return 0;
}

Short-Circuit Evaluation

C++ evaluates logical expressions left-to-right and stops as soon as the result is determined:

cpp
int x = 0;

// Short-circuit: if first is false, second is never evaluated
if (x != 0 && 10 / x > 2) {   // Safe — division is never reached if x == 0
    cout << "Safe division\n";
}

// Short-circuit: if first is true, second is never evaluated
if (x == 0 || riskyOperation()) {  // riskyOperation() not called when x == 0
    cout << "Short-circuited\n";
}

Assignment Operators

cpp
int x = 10;

x += 5;   // x = x + 5 = 15
x -= 3;   // x = x - 3 = 12
x *= 2;   // x = x * 2 = 24
x /= 4;   // x = x / 4 = 6
x %= 4;   // x = x % 4 = 2

// Bitwise assignment
x &= 0xFF;  // x = x & 0xFF
x |= 0x01;  // x = x | 0x01
x ^= 0x0F;  // x = x ^ 0x0F
x <<= 1;    // x = x << 1  (multiply by 2)
x >>= 1;    // x = x >> 1  (divide by 2)

Bitwise Operators

Bitwise operators work on individual bits — essential for embedded systems, flags, and performance-critical code:

cpp
#include <iostream>
using namespace std;

int main() {
    unsigned int a = 0b1010;   // 10 in binary
    unsigned int b = 0b1100;   // 12 in binary

    cout << (a & b)  << "\n";   // 8  (0b1000) — AND: both bits must be 1
    cout << (a | b)  << "\n";   // 14 (0b1110) — OR: at least one bit is 1
    cout << (a ^ b)  << "\n";   // 6  (0b0110) — XOR: bits differ
    cout << (~a)     << "\n";   // Bitwise NOT (flips all bits)
    cout << (a << 1) << "\n";   // 20 (0b10100) — left shift = multiply by 2
    cout << (a >> 1) << "\n";   // 5  (0b0101)  — right shift = divide by 2

    // Practical: using flags with bitwise operations
    unsigned int FLAGS = 0;
    const unsigned int READ_FLAG  = 0x01;   // 0b0001
    const unsigned int WRITE_FLAG = 0x02;   // 0b0010
    const unsigned int EXEC_FLAG  = 0x04;   // 0b0100

    FLAGS |= READ_FLAG | WRITE_FLAG;  // Set read + write
    cout << "Can read: " << (bool)(FLAGS & READ_FLAG)  << "\n";   // 1
    cout << "Can exec: " << (bool)(FLAGS & EXEC_FLAG)  << "\n";   // 0

    FLAGS &= ~WRITE_FLAG;  // Clear write flag
    cout << "Can write: " << (bool)(FLAGS & WRITE_FLAG) << "\n";  // 0

    return 0;
}

Ternary (Conditional) Operator

cpp
#include <iostream>
using namespace std;

int main() {
    int age = 20;

    // condition ? value_if_true : value_if_false
    string status = (age >= 18) ? "adult" : "minor";
    cout << status << "\n";   // adult

    // Nested ternary (use sparingly — reduces readability)
    int score = 75;
    string grade = (score >= 90) ? "A" :
                   (score >= 80) ? "B" :
                   (score >= 70) ? "C" : "F";
    cout << grade << "\n";    // C

    // Useful for inline minimum/maximum
    int a = 10, b = 20;
    int minVal = (a < b) ? a : b;  // min
    int maxVal = (a > b) ? a : b;  // max
    cout << minVal << " " << maxVal << "\n";   // 10 20

    return 0;
}

The sizeof Operator

cpp
#include <iostream>
using namespace std;

int main() {
    cout << sizeof(char)        << "\n";  // 1 byte
    cout << sizeof(short)       << "\n";  // 2 bytes
    cout << sizeof(int)         << "\n";  // 4 bytes
    cout << sizeof(long long)   << "\n";  // 8 bytes
    cout << sizeof(float)       << "\n";  // 4 bytes
    cout << sizeof(double)      << "\n";  // 8 bytes
    cout << sizeof(bool)        << "\n";  // 1 byte (typically)

    // sizeof on a variable works the same as sizeof on its type
    int x = 42;
    cout << sizeof(x) << "\n";  // 4

    // sizeof an array: total bytes, not element count
    int arr[10];
    cout << sizeof(arr) << "\n";              // 40
    cout << sizeof(arr) / sizeof(arr[0]) << "\n";  // 10 (element count)

    return 0;
}

Operator Precedence

Higher precedence operators are evaluated first:

PrecedenceOperatorsAssociativity
1 (highest)::Left
2(), [], ->, ., post ++/--Left
3Pre ++/--, !, ~, unary -, sizeof, new, deleteRight
4*, /, %Left
5+, -Left
6<<, >>Left
7<, <=, >, >=Left
8==, !=Left
9&Left
10^Left
11|Left
12&&Left
13||Left
14?:Right
15 (lowest)=, +=, -=, etc.Right
cpp
// Precedence examples
int result = 2 + 3 * 4;       // 14, not 20 (* before +)
int r2 = (2 + 3) * 4;         // 20 (parentheses override)
bool b = 5 > 3 && 10 < 20;    // true (> and < before &&)
int x = 1 + 2 << 2;           // (1+2) << 2 = 12 (+ before <<)

When in doubt, use parentheses. Explicit grouping is always clearer than relying on precedence rules.


Practical Example: Bitwise Permission System

cpp
#include <iostream>
#include <string>
using namespace std;

// Role-based permissions using bit flags
enum Permission : unsigned int {
    NONE    = 0,
    READ    = 1 << 0,  // 0001
    WRITE   = 1 << 1,  // 0010
    DELETE  = 1 << 2,  // 0100
    ADMIN   = 1 << 3   // 1000
};

bool hasPermission(unsigned int userPerms, unsigned int requiredPerm) {
    return (userPerms & requiredPerm) != 0;
}

void printPermissions(unsigned int perms) {
    cout << "READ:"   << hasPermission(perms, READ)   << " "
         << "WRITE:"  << hasPermission(perms, WRITE)  << " "
         << "DELETE:" << hasPermission(perms, DELETE) << " "
         << "ADMIN:"  << hasPermission(perms, ADMIN)  << "\n";
}

int main() {
    // Regular user: read and write only
    unsigned int userPerms = READ | WRITE;
    cout << "User permissions:\n";
    printPermissions(userPerms);
    // READ:1 WRITE:1 DELETE:0 ADMIN:0

    // Admin user: all permissions
    unsigned int adminPerms = READ | WRITE | DELETE | ADMIN;
    cout << "Admin permissions:\n";
    printPermissions(adminPerms);
    // READ:1 WRITE:1 DELETE:1 ADMIN:1

    // Grant delete to user
    userPerms |= DELETE;
    cout << "After granting delete:\n";
    printPermissions(userPerms);
    // READ:1 WRITE:1 DELETE:1 ADMIN:0

    // Revoke write
    userPerms &= ~WRITE;
    cout << "After revoking write:\n";
    printPermissions(userPerms);
    // READ:1 WRITE:0 DELETE:1 ADMIN:0

    // Toggle admin (XOR)
    adminPerms ^= ADMIN;
    cout << "After toggling admin:\n";
    printPermissions(adminPerms);
    // READ:1 WRITE:1 DELETE:1 ADMIN:0

    return 0;
}

Summary

  • Arithmetic: +, -, *, /, % — integer division truncates, use double cast for floating-point division
  • Increment/Decrement: ++i (pre) vs i++ (post) — prefer pre-increment in loops
  • Relational: ==, !=, <, >, <=, >= — return bool
  • Logical: &&, ||, ! — short-circuit evaluation skips unnecessary work
  • Assignment: +=, -=, *= etc. — compound assignment modifies in place
  • Bitwise: &, |, ^, ~, <<, >> — essential for flags, embedded systems, and performance
  • Ternary: condition ? a : b — a compact one-liner for simple if-else
  • Precedence: multiplication before addition; use parentheses when in doubt

Next, we'll explore C++ input and output — cin, cout, formatting with iomanip, and reading different data types from the user.