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
#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
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
#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.
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:
#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:
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
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:
#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
#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
#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:
| Precedence | Operators | Associativity |
|---|---|---|
| 1 (highest) | :: | Left |
| 2 | (), [], ->, ., post ++/-- | Left |
| 3 | Pre ++/--, !, ~, unary -, sizeof, new, delete | Right |
| 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 |
// 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
#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, usedoublecast for floating-point division - Increment/Decrement:
++i(pre) vsi++(post) — prefer pre-increment in loops - Relational:
==,!=,<,>,<=,>=— returnbool - 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.