Operators
Operators in C
📖 Introduction
Operators are symbols that tell the compiler to perform specific mathematical, logical, or other operations. C provides a rich set of operators that can be categorized into several groups based on their functionality.
🎯 Categories of Operators
C Operators
│
┌─────────┬───────────┬───┴───┬───────────┬──────────┐
│ │ │ │ │ │
Arithmetic Relational Logical Bitwise Assignment Other
│ │ │ │ │ │
+ - * == != && || & | = += sizeof
/ % < > <= ! ^ ~ -= *= ?: ,
++ -- >= << >> /= %= -> .
➕ Arithmetic Operators
Basic Arithmetic:
| Operator | Name | Example | Result |
|---|---|---|---|
+ | Addition | 5 + 3 | 8 |
- | Subtraction | 5 - 3 | 2 |
* | Multiplication | 5 * 3 | 15 |
/ | Division | 5 / 3 | 1 (integer division) |
% | Modulus (remainder) | 5 % 3 | 2 |
Integer Division vs Floating-Point Division:
int a = 5, b = 3;
printf("%d\n", a / b); // Output: 1 (truncated)
printf("%d\n", a % b); // Output: 2 (remainder)
float x = 5.0, y = 3.0;
printf("%.2f\n", x / y); // Output: 1.67
// Mixed: convert to float for float result
printf("%.2f\n", (float)a / b); // Output: 1.67
Increment and Decrement:
| Operator | Name | Example | Description |
|---|---|---|---|
++x | Pre-increment | y = ++x; | Increment x, then use |
x++ | Post-increment | y = x++; | Use x, then increment |
--x | Pre-decrement | y = --x; | Decrement x, then use |
x-- | Post-decrement | y = x--; | Use x, then decrement |
int x = 5;
int y;
// Pre-increment: increment first, then use
y = ++x; // x becomes 6, y becomes 6
x = 5; // Reset
// Post-increment: use first, then increment
y = x++; // y becomes 5, x becomes 6
// Common usage
for (int i = 0; i < 10; i++) { // i++ or ++i both work here
// loop body
}
Unary Plus and Minus:
int a = 5;
int b = -a; // b = -5
int c = +a; // c = 5 (rarely used, just for emphasis)
int d = -(-a); // d = 5 (negation of negation)
⚖️ Relational Operators
Relational operators compare two values and return 1 (true) or 0 (false).
| Operator | Name | Example | True When |
|---|---|---|---|
== | Equal to | a == b | a equals b |
!= | Not equal to | a != b | a not equal to b |
< | Less than | a < b | a is less than b |
> | Greater than | a > b | a is greater than b |
<= | Less than or equal | a <= b | a ≤ b |
>= | Greater than or equal | a >= b | a ≥ b |
Examples:
int a = 5, b = 10;
printf("%d\n", a == b); // 0 (false)
printf("%d\n", a != b); // 1 (true)
printf("%d\n", a < b); // 1 (true)
printf("%d\n", a > b); // 0 (false)
printf("%d\n", a <= 5); // 1 (true)
printf("%d\n", a >= 10); // 0 (false)
// Use in conditions
if (a < b) {
printf("a is less than b\n");
}
⚠️ Common Mistake: = vs ==
int x = 5;
// WRONG: Assignment, not comparison!
if (x = 10) { // Always true! x becomes 10
// ...
}
// CORRECT: Comparison
if (x == 10) { // True only if x equals 10
// ...
}
Floating-Point Comparison:
float a = 0.1 + 0.2;
float b = 0.3;
// DANGEROUS: May fail due to precision!
if (a == b) { // Might be false!
printf("Equal\n");
}
// SAFE: Use epsilon comparison
#define EPSILON 0.00001
if (fabs(a - b) < EPSILON) { // Better approach
printf("Approximately equal\n");
}
🔗 Logical Operators
Logical operators combine boolean expressions.
| Operator | Name | Example | True When |
| -------- | ----------- | -------- | --------------------- | --- | --- | --- | --------------------- |
| && | Logical AND | a && b | Both a AND b are true |
| | | | Logical OR | a | | b | Either a OR b is true |
| ! | Logical NOT | !a | a is false |
Truth Tables:
AND (&&):
| A | B | A && B |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 0 |
| 1 | 0 | 0 |
| 1 | 1 | 1 |
OR (||):
| A | B | A || B |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 1 |
NOT (!):
| A | !A |
|---|---|
| 0 | 1 |
| 1 | 0 |
Examples:
int age = 25;
int hasLicense = 1;
int isEmployed = 0;
// AND: Both conditions must be true
if (age >= 18 && hasLicense) {
printf("Can drive\n");
}
// OR: At least one condition must be true
if (hasLicense || age < 18) {
printf("Has license or is minor\n");
}
// NOT: Inverts the condition
if (!isEmployed) {
printf("Not employed\n");
}
// Complex condition
if ((age >= 18 && hasLicense) || age >= 65) {
printf("Eligible\n");
}
Short-Circuit Evaluation:
// AND: If first is false, second is NOT evaluated
if (ptr != NULL && *ptr > 0) { // Safe: won't dereference if NULL
// ...
}
// OR: If first is true, second is NOT evaluated
if (result != 0 || calculateFallback()) { // Fallback only if result is 0
// ...
}
// This can have side effects!
int x = 0;
if (x || ++x) { // ++x IS evaluated because x is 0
// x is now 1
}
🔢 Bitwise Operators
Bitwise operators work on individual bits of integers.
| Operator | Name | Example | Description |
| -------- | ----------- | -------- | ----------------------- | --- | -------------------- |
| & | AND | a & b | 1 if both bits are 1 |
| | | OR | a | b | 1 if either bit is 1 |
| ^ | XOR | a ^ b | 1 if bits are different |
| ~ | NOT | ~a | Inverts all bits |
| << | Left shift | a << n | Shift left by n bits |
| >> | Right shift | a >> n | Shift right by n bits |
Bitwise AND (&):
5 = 0101
3 = 0011
─────────
5 & 3 = 0001 = 1
Bitwise OR (|):
5 = 0101
3 = 0011
─────────
5 | 3 = 0111 = 7
Bitwise XOR (^):
5 = 0101
3 = 0011
─────────
5 ^ 3 = 0110 = 6
Bitwise NOT (~):
5 = 00000101
~5 = 11111010 = -6 (in two's complement)
Left Shift (<<):
5 = 0101
5 << 1 = 1010 = 10 (multiply by 2)
5 << 2 = 10100 = 20 (multiply by 4)
Right Shift (>>):
20 = 10100
20 >> 1 = 01010 = 10 (divide by 2)
20 >> 2 = 00101 = 5 (divide by 4)
Practical Examples:
// Check if number is even
if ((num & 1) == 0) {
printf("Even\n");
}
// Set a bit (bit 3)
flags |= (1 << 3);
// Clear a bit (bit 3)
flags &= ~(1 << 3);
// Toggle a bit (bit 3)
flags ^= (1 << 3);
// Check a bit (bit 3)
if (flags & (1 << 3)) {
printf("Bit 3 is set\n");
}
// Swap without temp variable
a ^= b;
b ^= a;
a ^= b;
// Fast multiply by power of 2
x <<= 3; // x *= 8
// Fast divide by power of 2
x >>= 2; // x /= 4
📝 Assignment Operators
| Operator | Example | Equivalent To |
| -------- | --------- | ------------- | ---- | ------ | --- |
| = | a = 5 | a = 5 |
| += | a += 5 | a = a + 5 |
| -= | a -= 5 | a = a - 5 |
| *= | a *= 5 | a = a * 5 |
| /= | a /= 5 | a = a / 5 |
| %= | a %= 5 | a = a % 5 |
| &= | a &= 5 | a = a & 5 |
| | = | a | = 5 | a = a | 5 |
| ^= | a ^= 5 | a = a ^ 5 |
| <<= | a <<= 2 | a = a << 2 |
| >>= | a >>= 2 | a = a >> 2 |
Examples:
int a = 10;
a += 5; // a = 15
a -= 3; // a = 12
a *= 2; // a = 24
a /= 4; // a = 6
a %= 4; // a = 2
// Chain assignment
int x, y, z;
x = y = z = 10; // All equal 10
// Compound with expression
int arr[10];
int i = 0;
arr[i++] = 100; // Assign to arr[0], then increment i
❓ Ternary Operator (?:)
The conditional operator provides a shorthand for if-else.
Syntax:
condition ? expression_if_true : expression_if_false
Examples:
int a = 5, b = 10;
// Simple usage
int max = (a > b) ? a : b; // max = 10
// In printf
printf("%s\n", (a > b) ? "a is greater" : "b is greater");
// Nested (not recommended for readability)
int result = (a > b) ? 1 : (a == b) ? 0 : -1;
// Assignment based on condition
int sign = (num > 0) ? 1 : (num < 0) ? -1 : 0;
// Common usage: null checking
char *name = (ptr != NULL) ? ptr->name : "Unknown";
📏 sizeof Operator
Returns the size in bytes of a type or variable.
printf("char: %zu bytes\n", sizeof(char)); // 1
printf("int: %zu bytes\n", sizeof(int)); // 4 (typically)
printf("double: %zu bytes\n", sizeof(double)); // 8
int arr[10];
printf("Array: %zu bytes\n", sizeof(arr)); // 40
printf("Elements: %zu\n", sizeof(arr)/sizeof(arr[0])); // 10
// Works with expressions
int x = 5;
printf("x: %zu bytes\n", sizeof(x));
printf("x+1.0: %zu bytes\n", sizeof(x + 1.0)); // 8 (promoted to double)
📍 Address and Dereference Operators
| Operator | Name | Usage |
|---|---|---|
& | Address-of | Get memory address |
* | Dereference | Access value at address |
int x = 42;
int *ptr = &x; // ptr holds address of x
printf("x = %d\n", x); // 42
printf("&x = %p\n", (void*)&x); // Address of x
printf("ptr = %p\n", (void*)ptr); // Same address
printf("*ptr = %d\n", *ptr); // 42 (value at address)
*ptr = 100; // Change x through pointer
printf("x = %d\n", x); // 100
🏗️ Member Access Operators
| Operator | Name | Usage |
|---|---|---|
. | Member access | struct.member |
-> | Pointer member access | pointer->member |
struct Point {
int x;
int y;
};
struct Point p = {10, 20};
struct Point *ptr = &p;
// Direct access
printf("x = %d\n", p.x);
// Pointer access
printf("x = %d\n", ptr->x); // Same as (*ptr).x
printf("y = %d\n", (*ptr).y); // Same as ptr->y
📐 Comma Operator
Evaluates multiple expressions, returns the last one.
int x, y, z;
// Multiple declarations (not comma operator)
int a = 1, b = 2, c = 3;
// Comma operator in expression
x = (y = 5, z = 10, y + z); // x = 15
// Common in for loops
for (i = 0, j = 10; i < j; i++, j--) {
printf("%d, %d\n", i, j);
}
📊 Operator Precedence
From highest to lowest precedence:
| Precedence | Operators | Associativity |
| ---------- | -------------------------------------------------- | ------------- | ------------- | ------------- |
| 1 | () [] -> . | Left to Right |
| 2 | ! ~ ++ -- + - * & sizeof (unary) | Right to Left |
| 3 | * / % | Left to Right |
| 4 | + - | Left to Right |
| 5 | << >> | Left to Right |
| 6 | < <= > >= | Left to Right |
| 7 | == != | Left to Right |
| 8 | & | Left to Right |
| 9 | ^ | Left to Right |
| 10 | | | Left to Right |
| 11 | && | Left to Right |
| 12 | | | | Left to Right |
| 13 | ?: | Right to Left |
| 14 | = += -= etc. | Right to Left |
| 15 | , | Left to Right |
Examples:
// Precedence affects evaluation order
int result = 2 + 3 * 4; // 14, not 20 (* before +)
int result2 = (2 + 3) * 4; // 20 (parentheses override)
// Associativity
int a = 10 - 5 - 2; // 3 (left to right: (10-5)-2)
int b = 2 ** 3 ** 4; // Error! ** is not a C operator
// Assignment is right to left
int x, y, z;
x = y = z = 5; // z=5, y=z, x=y
// Common pitfall
if (a = b == c) { // b == c first, then a = result
// ...
}
💡 When in doubt, use parentheses!
// Clear and unambiguous
if ((a > b) && (c < d)) { }
result = (a * b) + (c / d);
flags = (1 << bit) | (1 << other_bit);
⚠️ Common Mistakes
1. Assignment vs Comparison:
if (x = 5) // WRONG: Assignment (always true if non-zero)
if (x == 5) // CORRECT: Comparison
2. Integer Division Truncation:
float result = 5 / 2; // 2.0, not 2.5!
float result = 5.0 / 2; // 2.5 (correct)
float result = (float)5 / 2; // 2.5 (correct)
3. Precedence Issues:
if (a & b == c) // WRONG: == has higher precedence
if ((a & b) == c) // CORRECT
4. Increment in Complex Expressions:
int a = 5;
int b = a++ + a++; // UNDEFINED BEHAVIOR!
// Don't use ++ more than once per expression
✅ Best Practices
- •Use parentheses for clarity when mixing operators
- •Avoid complex expressions with multiple side effects
- •Don't mix signed and unsigned in comparisons
- •Use compound assignment (
+=,-=, etc.) when appropriate - •Be careful with increment/decrement in complex expressions
- •Compare floats with epsilon, not
==
⏭️ Next Topic
Continue to Typecasting to learn how to convert between data types.