C ProgrammingUnit 311 min read

Operators & Expressions: Types, Precedence, Side Effects & Real-World Math

Unit 3 of C Programming covers arithmetic/logical operators, operator precedence, expressions, type casting, and side effects with visual traces of how they work in memory and code—linked to real systems like eSewa’s transaction math and Ncell’s billing calculations.

TAKEAWAYS:

  • Arithmetic operators (+, -, *, /, %) perform math but can overflow or truncate; logical operators (&&, ||, !) evaluate to 1/0 and short-circuit.
  • Operator precedence and associativity determine evaluation order—always parenthesize to avoid surprises (e.g., a = b + c * d ≠ a = (b + c) * d).
  • Type casting forces conversions (e.g., (float)5/2 → 2.5), but implicit casting can lose precision (e.g., int/float → int).
  • Side effects (e.g., i++ vs ++i) and expressions (combinations of operators/operands) are the building blocks of all C logic.
  • Bitwise operators (&, |, ^, <<, >>) manipulate individual bits—critical for low-level tasks like network packet headers or file compression.
  • Expressions evaluate left-to-right unless parentheses or precedence rules dictate otherwise; tracing variable states step-by-step reveals bugs.

---

### **1. Arithmetic Operators: Math in C (and Why It Breaks)**
Arithmetic operators perform basic math, but C’s strict typing and integer division can cause unexpected results. Here’s how they work:

```mermaid
flowchart TD
    A["a = 5; b = 2;"] --> B["a + b → 7"]
    A --> C["a - b → 3"]
    A --> D["a * b → 10"]
    A --> E["a / b → 2 (integer division)"]
    A --> F["a % b → 1 (remainder)"]

Key Rules:

  • Division (/) truncates toward zero. For 5 / 2, result is 2 (not 2.5).
  • Modulus (%) returns the remainder. 5 % 2 = 1, but (-5) % 2 = -1 (sign follows dividend).
  • Overflow: Adding two large ints (e.g., INT_MAX + 1) wraps around to INT_MIN. Use long long for safety.

Worked Example: eSewa Transaction Fee Calculation eSewa charges a 2% fee on top-ups. If a user sends NPR 5000, the fee is calculated as:

float amount = 5000.0;
float fee = (amount * 2) / 100;  // 100.00
float total = amount + fee;      // 5100.00

Trace Table:

Step amount fee total
Start 5000.0 0.0 0.0
fee = ... 5000.0 100.0 0.0
total = ... 5000.0 100.0 5100.0

Why This Matters:

  • Using int for amount would truncate the fee to 0 (integer division).
  • Always use float or double for monetary calculations.

2. Logical Operators: Boolean Logic in Code

Logical operators (&&, ||, !) evaluate expressions to 1 (true) or 0 (false). They short-circuit:

  • &&: Stops if left operand is 0.
  • ||: Stops if left operand is 1.
flowchart TD
    A["x = 5; y = 0;"] --> B["x && y → 0 (short-circuits)"]
    A --> C["x || y → 1 (no short-circuit)"]
    A --> D["!x → 0"]

Worked Example: Ncell Bill Validation Ncell checks if a user’s balance (balance) is ≥ min_balance and their plan is active (is_active):

int balance = 1000;
int min_balance = 500;
int is_active = 1;
int can_use = (balance >= min_balance) && is_active;  // 1 (true)

Trace Table:

Condition Value Result
balance >= min_balance 1 1
is_active 1 1
can_use = 1 && 1 - 1

Short-Circuiting in Action:

int a = 5, b = 0;
int result = (a > 0) && (b++ > 0);  // b remains 0 (not incremented)

Here, b++ is never evaluated because a > 0 is already 1.


3. Operator Precedence: The Hidden Rules

C evaluates operators in a fixed order. Parentheses override precedence.

flowchart TD
    A["a = 5; b = 2; c = 3;"] --> B["a + b * c → 11 (b*c first)"]
    A --> C["(a + b) * c → 21 (parentheses first)"]
    A --> D["a = b = c → 3 (right-to-left)"]

Precedence Table (Highest to Lowest):

Category Operators Example
Postfix (), [], ++, -- x++
Unary +, -, !, (type) !x, (int)3.5
Multiplicative *, /, % a * b % c
Additive +, - a + b - c
Shift <<, >> a << 2
Relational <, >, <=, >= a > b
Equality ==, != a == b
Logical AND && a && b
Logical OR `
Ternary ? : a > b ? x : y
Assignment =, +=, -=, etc. a += b

Common Pitfall:

int x = 10;
int y = x++ + ++x;  // Undefined behavior!

Why?

  • x++ uses the old value (10) but increments x to 11.
  • ++x increments x again to 12 and uses 12.
  • Result: y = 10 + 12 = 22 (but compiler may optimize differently).

4. Type Casting: Forcing Conversions

C implicitly converts types (e.g., int to float), but explicit casting ((type)value) gives control.

flowchart TD
    A["int a = 5; float b = 2.5;"] --> B["a + b → 7.5 (implicit)"]
    A --> C["(int)(a + b) → 7 (explicit truncation)"]
    A --> D["(float)a / b → 2.0 (avoids integer division)"]

Worked Example: Daraz Discount Calculation Daraz offers a 15% discount on orders ≥ NPR 1000. For an order of NPR 1200:

int order = 1200;
float discount = (order >= 1000) ? (order * 0.15) : 0;
float final_price = order - discount;  // 1020.00

Trace Table:

Step discount final_price
order * 0.15 180.0 -
order - discount 180.0 1020.0

Key Point:

  • Without (float), order * 0.15 would truncate to 0 (integer division).

5. Bitwise Operators: Low-Level Control

Bitwise operators manipulate individual bits. Used in:

  • Network protocols (packet flags)
  • File compression
  • Hardware registers
flowchart TD
    A["int a = 5; (0101)"]
    A --> B["a & 3 → 1 (0101 & 0011 = 0001)"]
    A --> C["a | 2 → 7 (0101 | 0010 = 0111)"]
    A --> D["a << 1 → 10 (0101 << 1 = 1010)"]
    A --> E["a >> 1 → 2 (0101 >> 1 = 0010)"]

Worked Example: NTC Traffic Light Control NTC uses bitwise flags to control traffic lights. A status variable encodes:

  • Bit 0: Red light (1 = on)
  • Bit 1: Yellow light
  • Bit 2: Green light
int status = 0b001;  // Green light on (001)
status |= 0b010;     // Turn on yellow (001 | 010 = 011)
status &= ~(0b001);  // Turn off green (011 & 110 = 010)

Trace Table:

Operation status (binary) Meaning
Start 001 Green on
`status = 010` 011
status &= 110 010 Yellow only

6. Side Effects and Expressions

An expression combines operators/operands to produce a value. Side effects modify state (e.g., i++ changes i).

flowchart TD
    A["int i = 5;"] --> B["i++ → 5 (post-increment)"]
    A --> C["++i → 6 (pre-increment)"]
    A --> D["i += 2 → 7 (compound assignment)"]

Worked Example: Kathmandu Traffic Route Optimization A traffic system uses a loop to count cars passing a checkpoint:

int count = 0;
while (sensors[0].detect()) {
    count++;  // Side effect: increments count
}

Trace Table:

Iteration count sensors[0].detect() Action
1 0 1 (true) count++ → 1
2 1 1 (true) count++ → 2
3 2 0 (false) Exit loop

Key Difference:

  • count++ returns the old value (5) but increments afterward.
  • ++count increments first, then returns the new value (6).

7. Common Operator Pitfalls

Pitfall Example Fix
Integer division truncation 5 / 2 → 2 Use (float)5 / 2
Unintended short-circuiting a && b++ Parenthesize: (a) && (b++)
Bitwise vs logical operators a & b vs a && b && works on booleans only
Overflow in arithmetic INT_MAX + 1 Use long long
Undefined behavior in mixed ops a = b = c = 5 Assign sequentially

In the Real World

  1. eSewa Transaction Math

    • Idea Used: Arithmetic operators and type casting.
    • How? eSewa calculates fees, deductions, and final amounts using float to avoid truncation (e.g., (amount * fee_rate)). Implicit casting to int would lose cents.
  2. Ncell Billing System

    • Idea Used: Logical operators (&&, ||) and short-circuiting.
    • How? Ncell checks if a user can make a call by verifying:
      can_call = (balance >= min_balance) && (is_active) && (roaming_off);
      
      If balance is insufficient, the other checks are skipped (short-circuiting saves computation).
  3. Daraz Order Processing Queue

    • Idea Used: Compound assignment (+=, -=) and expressions.
    • How? Daraz updates inventory counts using:
      inventory[id] -= quantity;  // Side effect: modifies inventory
      
      This ensures real-time stock updates without manual recalculations.

Exam Tip

  1. Always Parenthesize:

    • Write (a + b) * c instead of a + b * c to avoid precedence errors.
    • Exam Question: "What is the output of printf("%d", 5 + 2 * 3)?" Answer: 11 (not 21). Parentheses change the result.
  2. Trace Step-by-Step:

    • For questions like "What is the value of x after x = 10; x = x++ + ++x;?", draw a table:
      Step x (old) x (new) Operation
      Start 10 - -
      x++ 10 11 Post-increment
      ++x 11 12 Pre-increment
      x = ... - 22 10 + 12
    • Answer: 22 (but note: this is undefined behavior in C; compilers may optimize differently).
  3. Watch for Type Casting:

    • Exam Question: "Why does printf("%d", 5 / 2) print 2 instead of 2.5?" Answer: Integer division truncates. Use (float)5 / 2 to get 2.5.
  4. Bitwise Operators in Flags:

    • Exam Question: "How would you toggle the 3rd bit of 0b1010?" Answer: 0b1010 ^ 0b0100 = 0b1110 (XOR flips the bit).
  5. Side Effects in Loops:

    • Exam Question: "What does this loop print?"
      for (int i = 0; i < 3; i++) printf("%d ", i++);
      
      Trace:
      Iteration i (old) i (new) Printed
      1 0 1 0
      2 1 2 1
      3 2 3 2
      Answer: 0 1 2 (but i becomes 3 after the loop).

Final Advice:

  • Memorize precedence (or use a cheat sheet during exams).
  • Always test edge cases (e.g., 0, negative numbers, overflow).
  • Draw tables for complex expressions—examiners love step-by-step reasoning.

Based on the TU BIM syllabus for C Programming (IT232), unit 3.

Discussion

Loading…