Docs

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:

OperatorNameExampleResult
+Addition5 + 38
-Subtraction5 - 32
*Multiplication5 * 315
/Division5 / 31 (integer division)
%Modulus (remainder)5 % 32

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:

OperatorNameExampleDescription
++xPre-incrementy = ++x;Increment x, then use
x++Post-incrementy = x++;Use x, then increment
--xPre-decrementy = --x;Decrement x, then use
x--Post-decrementy = 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).

OperatorNameExampleTrue When
==Equal toa == ba equals b
!=Not equal toa != ba not equal to b
<Less thana < ba is less than b
>Greater thana > ba is greater than b
<=Less than or equala <= ba ≤ b
>=Greater than or equala >= ba ≥ 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 (&&):

ABA && B
000
010
100
111

OR (||):

ABA || B
000
011
101
111

NOT (!):

A!A
01
10

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

OperatorNameUsage
&Address-ofGet memory address
*DereferenceAccess 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

OperatorNameUsage
.Member accessstruct.member
->Pointer member accesspointer->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

  1. Use parentheses for clarity when mixing operators
  2. Avoid complex expressions with multiple side effects
  3. Don't mix signed and unsigned in comparisons
  4. Use compound assignment (+=, -=, etc.) when appropriate
  5. Be careful with increment/decrement in complex expressions
  6. Compare floats with epsilon, not ==

⏭️ Next Topic

Continue to Typecasting to learn how to convert between data types.

Operators - C Programming Tutorial | DeepML