C++ Operators: Complete Guide with Examples
Master all C++ operators: arithmetic, relational, logical, bitwise, assignment, and special operators. Learn operator precedence, usage examples, and best practices.
Arithmetic
Math Operations
Relational
Comparison
Logical
Boolean Logic
Bitwise
Bit Operations
Introduction to C++ Operators
Operators are special symbols that perform operations on variables and values. C++ provides a rich set of operators that can be classified into several categories based on their functionality.
Operator Characteristics
- Arity: Number of operands (unary, binary, ternary)
- Precedence: Order of evaluation
- Associativity: Direction of evaluation (left-to-right or right-to-left)
- Overloadable: Can be redefined for user-defined types
Key Concepts
- Operand: Value/variable on which operator acts
- Expression: Combination of operators and operands
- Lvalue vs Rvalue: Location vs value distinction
- Side Effects: Changes to operands during evaluation
Basic Operator Example
Operators work with operands to form expressions: result = operand1 operator operand2
#include <iostream>
using namespace std;
int main() {
// Arithmetic operators
int a = 10, b = 3;
int sum = a + b; // 13
int diff = a - b; // 7
int product = a * b; // 30
int quotient = a / b; // 3 (integer division)
int remainder = a % b; // 1
// Relational operators
bool isEqual = (a == b); // false
bool isGreater = (a > b); // true
bool isNotEqual = (a != b); // true
// Logical operators
bool condition1 = true, condition2 = false;
bool andResult = condition1 && condition2; // false
bool orResult = condition1 || condition2; // true
bool notResult = !condition1; // false
cout << "Sum: " << sum << endl;
cout << "a == b: " << boolalpha << isEqual << endl;
cout << "condition1 && condition2: " << andResult << endl;
return 0;
}
Complete C++ Operators Reference
The following table provides a comprehensive reference of all C++ operators with examples and descriptions:
| Category | Operator | Name | Description & Example |
|---|---|---|---|
| Arithmetic Operators | |||
| Arithmetic | + | Addition | Adds two operands: a + b |
| Arithmetic | - | Subtraction | Subtracts second operand: a - b |
| Arithmetic | * | Multiplication | Multiplies two operands: a * b |
| Arithmetic | / | Division | Divides numerator by denominator: a / b |
| Arithmetic | % | Modulus | Remainder after division: a % b |
| Arithmetic | ++ | Increment | Increases value by 1: a++ (post), ++a (pre) |
| Arithmetic | -- | Decrement | Decreases value by 1: a-- (post), --a (pre) |
| Relational/Comparison Operators | |||
| Relational | == | Equal to | Checks equality: a == b returns true if equal |
| Relational | != | Not equal to | Checks inequality: a != b returns true if not equal |
| Relational | > | Greater than | a > b returns true if a is greater |
| Relational | < | Less than | a < b returns true if a is less |
| Relational | >= | Greater than or equal | a >= b returns true if a is greater or equal |
| Relational | <= | Less than or equal | a <= b returns true if a is less or equal |
| Logical Operators | |||
| Logical | && | Logical AND | a && b returns true if both are true |
| Logical | || | Logical OR | a || b returns true if either is true |
| Logical | ! | Logical NOT | !a returns true if a is false |
| Bitwise Operators | |||
| Bitwise | & | Bitwise AND | Sets each bit to 1 if both bits are 1: a & b |
| Bitwise | | | Bitwise OR | Sets each bit to 1 if either bit is 1: a | b |
| Bitwise | ^ | Bitwise XOR | Sets each bit to 1 if only one bit is 1: a ^ b |
| Bitwise | ~ | Bitwise NOT | Inverts all bits: ~a |
| Bitwise | << | Left shift | Shifts bits left, fills with 0: a << n |
| Bitwise | >> | Right shift | Shifts bits right: a >> n |
| Assignment Operators | |||
| Assignment | = | Simple assignment | Assigns right operand to left: a = b |
| Assignment | += | Add AND assign | a += b is same as a = a + b |
| Assignment | -= | Subtract AND assign | a -= b is same as a = a - b |
| Assignment | *= | Multiply AND assign | a *= b is same as a = a * b |
| Assignment | /= | Divide AND assign | a /= b is same as a = a / b |
| Assignment | %= | Modulus AND assign | a %= b is same as a = a % b |
| Assignment | &= | Bitwise AND assign | a &= b is same as a = a & b |
| Assignment | |= | Bitwise OR assign | a |= b is same as a = a | b |
| Assignment | ^= | Bitwise XOR assign | a ^= b is same as a = a ^ b |
| Assignment | <<= | Left shift AND assign | a <<= n is same as a = a << n |
| Assignment | >>= | Right shift AND assign | a >>= n is same as a = a >> n |
| Special Operators | |||
| Special | ?: | Ternary/Conditional | condition ? expr1 : expr2 - If condition true, expr1 else expr2 |
| Special | , | Comma | expr1, expr2 - Evaluates both, returns expr2 |
| Special | sizeof | Size of | sizeof(type) - Returns size in bytes |
| Special | typeid | Type identification | typeid(expr) - Returns type information |
| Special | static_cast | Static cast | static_cast<type>(expr) - Compile-time type conversion |
| Special | dynamic_cast | Dynamic cast | dynamic_cast<type>(expr) - Runtime type conversion (polymorphic) |
| Special | const_cast | Const cast | const_cast<type>(expr) - Adds/removes const qualifier |
| Special | reinterpret_cast | Reinterpret cast | reinterpret_cast<type>(expr) - Reinterprets bit pattern |
| Special | . and -> | Member access | obj.member (direct), ptr->member (via pointer) |
| Special | :: | Scope resolution | Class::member or namespace::member |
| Special | new | Dynamic memory allocation | new type - Allocates memory on heap |
| Special | delete | Dynamic memory deallocation | delete ptr - Frees heap memory |
| Special | () | Function call | function(arg1, arg2) - Calls a function |
| Special | [] | Array subscript | array[index] - Accesses array element |
Operator Examples with Code
Arithmetic Operators
#include <iostream>
using namespace std;
int main() {
int a = 15, b = 4;
cout << "a = " << a << ", b = " << b << endl;
cout << "a + b = " << a + b << endl; // 19
cout << "a - b = " << a - b << endl; // 11
cout << "a * b = " << a * b << endl; // 60
cout << "a / b = " << a / b << endl; // 3 (integer division)
cout << "a % b = " << a % b << endl; // 3
// Floating point division
double x = 15.0, y = 4.0;
cout << "x / y = " << x / y << endl; // 3.75
// Increment/Decrement
int count = 5;
cout << "count = " << count << endl;
cout << "count++ = " << count++ << endl; // Prints 5, then becomes 6
cout << "++count = " << ++count << endl; // Becomes 7, then prints 7
cout << "count-- = " << count-- << endl; // Prints 7, then becomes 6
cout << "--count = " << --count << endl; // Becomes 5, then prints 5
return 0;
}
Relational and Logical Operators
#include <iostream>
using namespace std;
int main() {
int age = 25;
double salary = 50000;
bool hasLicense = true;
// Relational operators
cout << boolalpha; // Print true/false instead of 1/0
cout << "age >= 18: " << (age >= 18) << endl; // true
cout << "salary < 60000: " << (salary < 60000) << endl; // true
cout << "age == 25: " << (age == 25) << endl; // true
cout << "age != 30: " << (age != 30) << endl; // true
// Logical operators
bool canDrive = (age >= 18) && hasLicense;
bool isEligible = (age > 21) || (salary > 40000);
bool isInvalid = !(age > 0);
cout << "Can drive: " << canDrive << endl; // true
cout << "Is eligible: " << isEligible << endl; // true
cout << "Is invalid age: " << isInvalid << endl; // false
// Short-circuit evaluation
int x = 5, y = 0;
bool result = (y != 0) && (x / y > 2); // Safe due to short-circuit
cout << "Short-circuit result: " << result << endl;
return 0;
}
Bitwise Operators
#include <iostream>
#include <bitset> // For binary display
using namespace std;
int main() {
unsigned char a = 0b00110101; // 53 in decimal
unsigned char b = 0b00001111; // 15 in decimal
cout << "a = " << bitset<8>(a) << " (" << (int)a << ")" << endl;
cout << "b = " << bitset<8>(b) << " (" << (int)b << ")" << endl;
// Bitwise operations
cout << "a & b = " << bitset<8>(a & b) << " (" << (int)(a & b) << ")" << endl;
cout << "a | b = " << bitset<8>(a | b) << " (" << (int)(a | b) << ")" << endl;
cout << "a ^ b = " << bitset<8>(a ^ b) << " (" << (int)(a ^ b) << ")" << endl;
cout << "~a = " << bitset<8>(~a) << " (" << (int)(~a) << ")" << endl;
// Shift operations
cout << "a << 2 = " << bitset<8>(a << 2) << " (" << (int)(a << 2) << ")" << endl;
cout << "a >> 2 = " << bitset<8>(a >> 2) << " (" << (int)(a >> 2) << ")" << endl;
// Common bitwise uses
int flags = 0;
const int FLAG_A = 1 << 0; // 0001
const int FLAG_B = 1 << 1; // 0010
const int FLAG_C = 1 << 2; // 0100
const int FLAG_D = 1 << 3; // 1000
// Set flags
flags |= FLAG_A; // Set flag A
flags |= FLAG_C; // Set flag C
// Check flag
if (flags & FLAG_A) {
cout << "Flag A is set" << endl;
}
// Clear flag
flags &= ~FLAG_C; // Clear flag C
// Toggle flag
flags ^= FLAG_B; // Toggle flag B
return 0;
}
Operator Precedence and Associativity
Operator precedence determines the order of evaluation in expressions. When operators have the same precedence, associativity determines the direction of evaluation.
Important Rule
Use parentheses () to explicitly specify evaluation order when in doubt. This improves readability and prevents bugs.
| Precedence | Category | Operators | Associativity |
|---|---|---|---|
| 1 (Highest) | Scope resolution | :: |
Left to right |
| 2 | Postfix | () [] -> . ++ -- |
Left to right |
| 3 | Unary | ++ -- + - ! ~ (type) * & sizeof |
Right to left |
| 4 | Member access | .* ->* |
Left to right |
| 5 | Multiplicative | * / % |
Left to right |
| 6 | Additive | + - |
Left to right |
| 7 | Bitwise shift | << >> |
Left to right |
| 8 | Relational | < <= > >= |
Left to right |
| 9 | Equality | == != |
Left to right |
| 10 | Bitwise AND | & |
Left to right |
| 11 | Bitwise XOR | ^ |
Left to right |
| 12 | Bitwise OR | | |
Left to right |
| 13 | Logical AND | && |
Left to right |
| 14 | Logical OR | || |
Left to right |
| 15 | Conditional | ?: |
Right to left |
| 16 | Assignment | = += -= *= /= %= &= ^= |= <<= >>= |
Right to left |
| 17 (Lowest) | Comma | , |
Left to right |
#include <iostream>
using namespace std;
int main() {
int a = 5, b = 10, c = 15;
// Without parentheses (rely on precedence)
int result1 = a + b * c; // 5 + (10 * 15) = 155
int result2 = a * b + c; // (5 * 10) + 15 = 65
int result3 = a > b && b < c; // (5 > 10) && (10 < 15) = false
// With parentheses (explicit order)
int result4 = (a + b) * c; // (5 + 10) * 15 = 225
int result5 = a * (b + c); // 5 * (10 + 15) = 125
bool result6 = (a > b) && (b < c); // false
cout << "a + b * c = " << result1 << endl;
cout << "a * b + c = " << result2 << endl;
cout << "a > b && b < c = " << boolalpha << result3 << endl;
cout << "(a + b) * c = " << result4 << endl;
cout << "a * (b + c) = " << result5 << endl;
// Common precedence pitfalls
int x = 5;
int y = x++ + ++x; // Undefined behavior! Avoid multiple increments in same expression
cout << "y (undefined behavior): " << y << endl;
return 0;
}
x++ + ++x). The result is undefined and varies between compilers.
Special Operators in Detail
Ternary Operator
condition ? expr1 : expr2 - Compact if-else replacement.
int max = (a > b) ? a : b;
string result = (score >= 50)
? "Pass" : "Fail";
sizeof Operator
Returns size in bytes of type or variable.
sizeof(int); // 4 (usually)
sizeof(double); // 8 (usually)
int arr[10];
sizeof(arr); // 40 (if int is 4 bytes)
Type Cast Operators
C++ offers four casting operators for type conversion.
// Static cast (compile-time)
double d = 3.14;
int i = static_cast(d);
// Dynamic cast (runtime, polymorphic)
Base* b = new Derived();
Derived* d = dynamic_cast(b);
new and delete
Dynamic memory allocation and deallocation.
// Single object
int* ptr = new int(42);
delete ptr;
// Array
int* arr = new int[10];
delete[] arr;
#include <iostream>
#include <typeinfo>
using namespace std;
int main() {
// Ternary operator
int age = 20;
string status = (age >= 18) ? "Adult" : "Minor";
cout << "Status: " << status << endl;
// Comma operator
int a = (cout << "Hello, ", 5);
cout << "\na = " << a << endl;
// sizeof operator
cout << "Size of int: " << sizeof(int) << " bytes" << endl;
cout << "Size of double: " << sizeof(double) << " bytes" << endl;
// typeid operator
cout << "Type of age: " << typeid(age).name() << endl;
cout << "Type of status: " << typeid(status).name() << endl;
// Scope resolution
int value = 100;
{
int value = 200; // Shadows outer value
cout << "Inner value: " << value << endl;
cout << "Outer value: " << ::value << endl; // Access global/namespace
}
// Member access operators
struct Point {
int x, y;
};
Point p = {10, 20};
Point* ptr = &p;
cout << "p.x = " << p.x << endl; // Direct member access
cout << "ptr->y = " << ptr->y << endl; // Indirect member access
return 0;
}
Best Practices and Common Pitfalls
Do's
- Use parentheses for complex expressions
- Use compound assignment operators for brevity
- Prefer prefix increment/decrement (++i) when value not needed
- Use
constwith pointers to prevent modification - Check for division by zero before division/modulus
- Use bitwise operators only for bit manipulation
- Initialize variables before using them
Don'ts
- Don't use assignment (=) instead of equality (==)
- Don't modify same variable multiple times in one expression
- Don't use bitwise operators as logical operators
- Don't forget operator precedence (use parentheses)
- Don't use C-style casts (use C++ casts instead)
- Don't use
sizeofon pointer types for array size - Don't ignore compiler warnings about type conversions
if (x = 5)instead ofif (x == 5)(assignment vs comparison)a < b < cinstead of(a < b) && (b < c)- Integer division when expecting floating-point:
5/2 = 2not2.5 - Using
!(logical NOT) instead of~(bitwise NOT) - Forgetting that
&&and||use short-circuit evaluation
#include <iostream>
using namespace std;
int main() {
// GOOD PRACTICES
// 1. Use parentheses for clarity
int result = (2 + 3) * 4; // Clear: (2+3)*4 = 20
// 2. Use compound assignment
int total = 0;
total += 5; // Better than: total = total + 5
total *= 2; // Better than: total = total * 2
// 3. Check before division
int numerator = 10, denominator = 0;
if (denominator != 0) {
int quotient = numerator / denominator;
} else {
cout << "Error: Division by zero!" << endl;
}
// 4. Use prefix increment when value not needed
for (int i = 0; i < 10; ++i) { // ++i is slightly faster than i++
cout << i << " ";
}
cout << endl;
// 5. Avoid common mistakes
int x = 5, y = 10;
// WRONG: if (x = y) // Assigns y to x, always true if y != 0
// RIGHT:
if (x == y) {
cout << "x equals y" << endl;
}
// WRONG: if (0 < x < 5) // Doesn't work as expected
// RIGHT:
if (x > 0 && x < 5) {
cout << "x is between 0 and 5" << endl;
}
// 6. Use appropriate casts
double pi = 3.14159;
int intPi = static_cast<int>(pi); // Explicit conversion
cout << "Integer part of pi: " << intPi << endl;
return 0;
}