Skip to content

Operators

Operators are symbols that perform operations on operands (values, variables, or expressions). Understanding operators is essential for writing effective C++ code. This chapter covers all the operators in C++ and demonstrates how to use them effectively.

C++ provides a rich set of operators organized into several categories:

┌─────────────────────────────────────────────────────────────┐
│ C++ Operators │
├─────────────────────────────────────────────────────────────┤
│ Arithmetic → + - * / % ++ -- │
│ Comparison → == != < > <= >= │
│ Logical → && || ! │
│ Bitwise → & | ^ ~ << >> │
│ Assignment → = += -= *= /= %= <<= >>= &= |= │
│ Ternary → ?: │
│ Member Access → . -> [] () │
│ Type Cast → static_cast dynamic_cast etc. │
│ Other → sizeof , (type) new delete │
└─────────────────────────────────────────────────────────────┘

These operators perform mathematical calculations.

int a = 10, b = 3;
int sum = a + b; // 13
int diff = a - b; // 7
int prod = a * b; // 30
int quot = a / b; // 3 (integer division)
int rem = a % b; // 1 (remainder)
// Integer division (truncates toward zero)
int x = 7 / 2; // 3
int y = -7 / 2; // -3
// Floating-point division
double d1 = 7.0 / 2; // 3.5
double d2 = 7 / 2.0; // 3.5
double d3 = 7.0 / 2.0; // 3.5
int x = 5;
// Prefix (increment, then use)
int a = ++x; // x becomes 6, a is 6
// Postfix (use, then increment)
int y = 5;
int b = y++; // b is 5, y becomes 6
// Same with decrement
int z = 5;
--z; // z is 4
z--; // z becomes 3

Common Pitfall:

// Avoid in production code (confusing)
int i = 0;
std::cout << i++ << std::endl; // Prints 0, then i becomes 1
// Prefer:
int i = 0;
std::cout << i << std::endl;
i++;

Assignment operators assign values to variables.

int x = 10; // Basic assignment
x = 20; // Change value

These operators combine an operation with assignment:

int x = 10;
x += 5; // x = x + 5 → x is 15
x -= 5; // x = x - 5 → x is 10
x *= 2; // x = x * 2 → x is 20
x /= 4; // x = x / 4 → x is 5
x %= 3; // x = x % 3 → x is 2
x <<= 2; // x = x << 2 (bitwise left shift)
x >>= 1; // x = x >> 1 (bitwise right shift)
x &= 0xF; // x = x & 0xF (bitwise AND)
x |= 0xF; // x = x | 0xF (bitwise OR)
x ^= 0xF; // x = x ^ 0xF (bitwise XOR)

These operators compare two values and return a boolean result.

int a = 10, b = 20;
bool equal = (a == b); // false
bool notEqual = (a != b); // true
bool less = (a < b); // true
bool greater = (a > b); // false
bool lessEq = (a <= b); // true
bool greaterEq = (a >= b); // false

Important: Don’t confuse = (assignment) with == (comparison)!

// Common bug
if (x = 5) { // OOPS! Assigns 5 to x, always true
// This always executes
}
// Correct
if (x == 5) {
// This executes only if x equals 5
}

Logical operators combine boolean expressions.

bool a = true, b = false;
bool andResult = a && b; // false (both must be true)
bool orResult = a || b; // true (at least one is true)
bool notResult = !a; // false (negation)
bool notResult2 = !b; // true

C++ evaluates expressions lazily:

// If first is false, second is not evaluated (AND)
if (ptr != nullptr && ptr->value > 0) {
// Safe: ptr->value only accessed if ptr is not null
}
// If first is true, second is not evaluated (OR)
if (ptr == nullptr || ptr->value == 0) {
// Handles null case
}

Bitwise operators manipulate individual bits in integer types.

OperatorNameDescription
&AND1 if both bits are 1
|OR1 if either bit is 1
^XOR1 if bits are different
~NOTInverts all bits
<<LSHIFTShifts bits left
>>RSHIFTShifts bits right
unsigned int x = 5; // Binary: 0101
unsigned int y = 3; // Binary: 0011
unsigned int andResult = x & y; // 0001 = 1
unsigned int orResult = x | y; // 0111 = 7
unsigned int xorResult = x ^ y; // 0110 = 6
unsigned int notResult = ~x; // ...11111010 (all bits inverted)
unsigned int leftShift = x << 1; // 1010 = 10 (multiply by 2)
unsigned int rightShift = x >> 1; // 0010 = 2 (divide by 2)
// Check if a bit is set
bool bit3Set = (value & (1 << 3)) != 0;
// Set a bit
value |= (1 << 3);
// Clear a bit
value &= ~(1 << 3);
// Toggle a bit
value ^= (1 << 3);
// Check multiple flags
if (options & (FLAG_A | FLAG_B)) {
// Either flag is set
}

The ternary operator ?: is a compact if-else:

// condition ? value_if_true : value_if_false
int age = 20;
std::string status = (age >= 18) ? "adult" : "minor";
// status is "adult"
// Equivalent if-else:
std::string status;
if (age >= 18) {
status = "adult";
} else {
status = "minor";
}

Nested ternary (avoid for readability):

// Hard to read
char grade = (score >= 90) ? 'A' :
(score >= 80) ? 'B' :
(score >= 70) ? 'C' :
(score >= 60) ? 'D' : 'F';

Operators have different precedence (priority) levels:

PrecedenceOperatorsAssociativity
1 (highest)() [] -> .Left to right
2! ~ ++ -- + - (unary) *(dereference) &(address) sizeof new deleteRight to left
3* / %Left to right
4+ - (binary)Left to right
5<< >>Left to right
6< <= > >=Left to right
7== !=Left to right
8& (bitwise AND)Left to right
9^Left to right
10|Left to right
11&&Left to right
12||Left to right
13?:Right to left
14= += -= *= /= %= <<= >>= &= |= ^=Right to left
15 (lowest),Left to right

Best Practice: Use parentheses to make precedence explicit:

// Clear
int result = (a + b) * c;
// Could be confusing without parentheses
int result = a + b * c; // a + (b * c)

The comma operator evaluates expressions left to right and returns the last value:

int a, b;
// In for loops (common use)
for (int i = 0, j = 10; i < j; i++, j--) {
// i and j are both updated
}
// As expression (avoid - reduces readability)
int x = (a = 5, b = 10, a + b); // x is 15

Returns the size of a type or variable in bytes:

int arr[10];
sizeof(int); // 4 (typically)
sizeof(arr); // 40 (entire array)
sizeof(arr) / sizeof(arr[0]); // 10 (array length)
struct Point {
int x;
int y;
};
Point p{10, 20};
// Direct member access
p.x = 15;
// Pointer member access
Point* ptr = &p;
ptr->x = 25; // Same as (*ptr).x
// Subscript operator
int arr[5] = {1, 2, 3, 4, 5};
int first = arr[0];

Modern C++ provides safer casting operators:

double d = 3.14159;
// static_cast - for well-defined conversions
int i = static_cast<int>(d); // 3
// const_cast - add or remove const
const int ci = 42;
int* pi = const_cast<int*>(&ci);
// reinterpret_cast - low-level reinterpretation
int* ip = reinterpret_cast<int*>(&d);
// dynamic_cast - for polymorphic types
class Base { virtual void f() {} };
class Derived : public Base {};
Base* base = new Derived;
Derived* derived = dynamic_cast<Derived*>(base);

An expression is a combination of operators and operands that evaluates to a value:

// Simple expressions
42 // Literal expression
x // Variable expression
x + y // Arithmetic expression
x > 0 // Relational expression
x && y // Logical expression
// Complex expressions
int result = (x + y) * (a - b) / 2;
bool valid = (x >= 0) && (x <= 100) && (ptr != nullptr);
// Expression with side effects
int i = ++x + y--; // Increments x, decrements y

You can define custom behavior for operators with user-defined types (covered in OOP section):

class Vector {
public:
double x, y;
// Overload + operator
Vector operator+(const Vector& other) {
return {x + other.x, y + other.y};
}
};
Vector v1{1, 2}, v2{3, 4};
Vector v3 = v1 + v2; // Uses overloaded +
// Unclear
int result = a + b * c - d / e;
// Clear
int result = ((a + (b * c)) - (d / e));
// Avoid
std::string grade = score >= 90 ? "A" : score >= 80 ? "B" : score >= 70 ? "C" : "D";
// Prefer
if (score >= 90) grade = "A";
else if (score >= 80) grade = "B";
// etc.
// Clear
x += 5;
// Less clear
x = x + 5;

4. Be Careful with Bitwise on Signed Types

Section titled “4. Be Careful with Bitwise on Signed Types”
// Use unsigned for bitwise operations
unsigned int flags = 0xFF;
unsigned int mask = 0x0F;
unsigned int result = flags & mask;
// Signed behavior is implementation-defined
int signedValue = -1;
unsigned int result2 = signedValue >> 1; // Undefined behavior!
#include <iostream>
int main() {
// Arithmetic
int a = 10, b = 3;
std::cout << "Arithmetic:" << std::endl;
std::cout << " a + b = " << (a + b) << std::endl;
std::cout << " a - b = " << (a - b) << std::endl;
std::cout << " a * b = " << (a * b) << std::endl;
std::cout << " a / b = " << (a / b) << std::endl;
std::cout << " a % b = " << (a % b) << std::endl;
// Comparison
std::cout << "\nComparison:" << std::endl;
std::cout << " a == b: " << (a == b) << std::endl;
std::cout << " a != b: " << (a != b) << std::endl;
std::cout << " a < b: " << (a < b) << std::endl;
std::cout << " a > b: " << (a > b) << std::endl;
// Logical
bool x = true, y = false;
std::cout << "\nLogical:" << std::endl;
std::cout << " x && y: " << (x && y) << std::endl;
std::cout << " x || y: " << (x || y) << std::endl;
std::cout << " !x: " << (!x) << std::endl;
// Bitwise
unsigned int m = 5, n = 3;
std::cout << "\nBitwise:" << std::endl;
std::cout << " m & n: " << (m & n) << std::endl;
std::cout << " m | n: " << (m | n) << std::endl;
std::cout << " m ^ n: " << (m ^ n) << std::endl;
std::cout << " ~m: " << (~m) << std::endl;
std::cout << " m << 1: " << (m << 1) << std::endl;
std::cout << " m >> 1: " << (m >> 1) << std::endl;
// Ternary
int score = 85;
std::cout << "\nTernary:" << std::endl;
std::cout << " Grade: " << (score >= 60 ? "Pass" : "Fail") << std::endl;
return 0;
}
  • C++ provides arithmetic, comparison, logical, and bitwise operators
  • Operator precedence determines the order of evaluation; use parentheses to be clear
  • The increment/decrement operators (++, --) come in prefix and postfix forms
  • Be careful not to confuse = (assignment) with == (comparison)
  • Bitwise operators are powerful for flag manipulation and low-level programming
  • Modern C++ prefers static_cast over C-style casts
  • Keep expressions simple and readable

Now let’s learn about control flow statements in C++.

Next Chapter: 06_control_flow.md - Control Flow Statements