CoreTechED

Loading

Learn C operator precedence and associativity: complete table, examples, and common pitfalls. Master expression evaluation order in C programming.

Operator Precedence and Associativity in C: Guide

Operator Precedence and Associativity in C: A Comprehensive Tutorial

Operator precedence and associativity are fundamental rules that determine the order in which operators are evaluated in C expressions. Understanding these rules is essential for writing correct and predictable code, avoiding subtle bugs, and interpreting expressions as the compiler does.

This tutorial provides an in-depth exploration of operator precedence and associativity in C: their definitions, the complete precedence table, practical examples, common pitfalls, and best practices.


Conceptual Introduction & Fundamentals

What is Operator Precedence?

Operator precedence determines which operator is evaluated first when an expression contains multiple operators. Operators with higher precedence are evaluated before operators with lower precedence.

Example:

c

int result = 10 + 20 * 30;

In this expression, * has higher precedence than +, so multiplication is performed first:

text

10 + (20 * 30) = 10 + 600 = 610

Not:

text

(10 + 20) * 30 = 30 * 30 = 900

What is Operator Associativity?

Associativity determines the order of evaluation when two or more operators have the same precedence. Associativity can be:

  • Left-to-Right (L→R): Operations are evaluated from left to right.
  • Right-to-Left (R→L): Operations are evaluated from right to left.

Example (Left-to-Right):

c

int result = 100 / 5 % 2;

Both / and % have the same precedence. They are left-associative:

text

(100 / 5) % 2 = 20 % 2 = 0

Example (Right-to-Left):

c

int a, b, c; a = b = c = 10;

Assignment = is right-associative:

text

a = (b = (c = 10)) = a = (b = 10) = a = 10

Why Precedence and Associativity Matter

  • Correctness: Misunderstanding precedence leads to incorrect results.
  • Predictability: Knowing the rules ensures your code behaves as intended.
  • Readability: Using parentheses to clarify intent (even when not strictly needed) makes code easier to understand.
  • Portability: Precedence and associativity are standardized in C, so code behaves consistently across compilers.

Evaluation Order vs. Precedence

Precedence and associativity determine the grouping of operands with operators. They do not determine the order of evaluation of operands. For example:

c

int result = f1() + f2() * f3();

Precedence says f2() * f3() is grouped together, but the order in which f1(), f2(), and f3() are called is unspecified.


Syntax & Core Mechanics

The Complete Operator Precedence Table

The following table lists C operators from highest to lowest precedence, along with their associativity:

PrecedenceOperatorDescriptionAssociativity
1 (highest)()Function callLeft-to-Right
[]Array subscript
.Structure member access
->Structure pointer member access
++, --Postfix increment/decrement
2++, --Prefix increment/decrementRight-to-Left
+, -Unary plus/minus
!, ~Logical NOT, Bitwise NOT
(type)Cast operator
*Dereference
&Address-of
sizeofSize in bytes
3*, /, %Multiplication, division, modulusLeft-to-Right
4+, -Addition, subtractionLeft-to-Right
5<<, >>Bitwise shift left/rightLeft-to-Right
6<, <=, >, >=Relational operatorsLeft-to-Right
7==, !=Equality operatorsLeft-to-Right
8&Bitwise ANDLeft-to-Right
9^Bitwise XORLeft-to-Right
10|Bitwise ORLeft-to-Right
11&&Logical ANDLeft-to-Right
12||Logical ORLeft-to-Right
13?:Ternary conditionalRight-to-Left
14=AssignmentRight-to-Left
+=, -=, *=, /=, %=Compound assignment
<<=, >>=, &=, ^=, |=Compound assignment
15 (lowest),Comma (expression separator)Left-to-Right

Memory Trick: PUMA’S REBL TAC

An easy way to remember the precedence order:

P = Postfix
U = Unary
M = Multiplicative
A = Additive
S = Shift
R = Relational
E = Equality
B = Bitwise
L = Logical
T = Ternary
A = Assignment
C = Comma

Detailed Examples

Example 1: Multiplicative vs. Additive

c

#include <stdio.h> int main(void) { int a = 6, b = 3, c = 4; int res; // * and / have higher precedence than + // * and / are left-associative res = a + b * c / 2; // Evaluation: a + ((b * c) / 2) = 6 + ((3 * 4) / 2) = 6 + 6 = 12 printf("%d\n", res); return 0; }

Expected output:

text

12

Example 2: Left-to-Right Associativity

c

#include <stdio.h> int main(void) { // / and % have same precedence, left-to-right int result = 100 / 5 % 2; // (100 / 5) % 2 = 20 % 2 = 0 printf("%d\n", result); return 0; }

Expected output:

text

0

Example 3: Right-to-Left Associativity

c

#include <stdio.h> int main(void) { int a, b, c; // Assignment is right-associative a = b = c = 10; printf("a = %d, b = %d, c = %d\n", a, b, c); return 0; }

Expected output:

text

a = 10, b = 10, c = 10

Example 4: Using Parentheses to Change Order

c

#include <stdio.h> int main(void) { int result1 = 100 + 200 / 10 - 3 * 10; // 100 + (200/10) - (3*10) = 100 + 20 - 30 = 90 int result2 = (100 + 200) / 10 - 3 * 10; // (300) / 10 - 30 = 30 - 30 = 0 printf("result1 = %d\n", result1); printf("result2 = %d\n", result2); return 0; }

Expected output:

text

result1 = 90 result2 = 0

Example 5: Postfix vs. Prefix Increment

c

#include <stdio.h> int main(void) { int a = 5; int b = 5; // Postfix ++ has higher precedence than prefix ++ int result1 = a++ + ++a; // Undefined behavior (side effects on same variable) // Avoid such expressions! // Better: separate statements int c = 5; int d = c++; // d = 5, c = 6 int e = ++c; // e = 7, c = 7 printf("d = %d, e = %d, c = %d\n", d, e, c); return 0; }

Expected output:

text

d = 5, e = 7, c = 7

Full Practical Code Example with Explanation

This program demonstrates operator precedence and associativity in a realistic scenario: a mathematical expression evaluator that shows step-by-step evaluation.

Program: Expression Evaluation Demonstrator

c

/** * precedence_demo.c * Demonstrates operator precedence and associativity in C. * Evaluates various expressions and shows the order of operations. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #define MAX_LINE_LEN 100 /* Function prototypes */ void demonstratePrecedence(void); void demonstrateAssociativity(void); void demonstrateParentheses(void); void demonstrateCommonMistakes(void); int main(void) { int choice; char buffer[MAX_LINE_LEN]; while (1) { printf("\n=== Operator Precedence and Associativity ===\n"); printf("1. Demonstrate Precedence\n"); printf("2. Demonstrate Associativity\n"); printf("3. Demonstrate Parentheses Effect\n"); printf("4. Demonstrate Common Mistakes\n"); printf("5. Exit\n"); printf("Enter choice: "); fflush(stdout); if (fgets(buffer, sizeof(buffer), stdin) == NULL) { fprintf(stderr, "Error reading choice.\n"); return EXIT_FAILURE; } choice = atoi(buffer); switch (choice) { case 1: demonstratePrecedence(); break; case 2: demonstrateAssociativity(); break; case 3: demonstrateParentheses(); break; case 4: demonstrateCommonMistakes(); break; case 5: printf("Exiting.\n"); return EXIT_SUCCESS; default: printf("Invalid choice. Please try again.\n"); break; } } return EXIT_SUCCESS; } /* Demonstrates precedence of different operator categories */ void demonstratePrecedence(void) { printf("\n=== Operator Precedence ===\n"); /* Multiplicative vs. Additive */ int a = 10 + 20 * 30; printf("10 + 20 * 30 = %d\n", a); printf(" (20 * 30 evaluated first, then + 10)\n"); /* Shift vs. Relational */ int b = 5 << 2 > 10; printf("5 << 2 > 10 = %d\n", b); printf(" ((5 << 2) > 10) = (20 > 10) = 1\n"); /* Relational vs. Equality */ int c = 5 > 3 == 1; printf("5 > 3 == 1 = %d\n", c); printf(" ((5 > 3) == 1) = (1 == 1) = 1\n"); /* Bitwise vs. Logical */ int d = 5 & 3 == 3; printf("5 & 3 == 3 = %d\n", d); printf(" (5 & (3 == 3)) = (5 & 1) = 1\n"); printf(" Note: == has higher precedence than &\n"); int e = (5 & 3) == 3; printf("(5 & 3) == 3 = %d\n", e); printf(" ((5 & 3) == 3) = (1 == 3) = 0\n"); /* Logical AND vs. Logical OR */ int f = 1 || 0 && 0; printf("1 || 0 && 0 = %d\n", f); printf(" (1 || (0 && 0)) = (1 || 0) = 1\n"); } /* Demonstrates associativity of operators */ void demonstrateAssociativity(void) { printf("\n=== Operator Associativity ===\n"); /* Left-to-right: / and % */ int a = 100 / 5 % 2; printf("100 / 5 %% 2 = %d\n", a); printf(" ((100 / 5) %% 2) = (20 %% 2) = 0\n"); /* Left-to-right: - and + */ int b = 10 - 3 + 2; printf("10 - 3 + 2 = %d\n", b); printf(" ((10 - 3) + 2) = (7 + 2) = 9\n"); /* Right-to-left: assignment */ int x, y, z; x = y = z = 42; printf("x = y = z = 42 => x=%d, y=%d, z=%d\n", x, y, z); printf(" (x = (y = (z = 42)))\n"); /* Right-to-left: ternary */ int c = 5 > 3 ? 10 : 20 > 10 ? 30 : 40; printf("5 > 3 ? 10 : 20 > 10 ? 30 : 40 = %d\n", c); printf(" (5 > 3 ? 10 : (20 > 10 ? 30 : 40)) = 10\n"); /* Right-to-left: unary */ int d = 5; int e = -~d; // ~d = -6, then unary minus = 6 printf("-~5 = %d\n", e); printf(" (-(~5)) = (-(-6)) = 6\n"); } /* Demonstrates how parentheses change evaluation order */ void demonstrateParentheses(void) { printf("\n=== Parentheses Effect ===\n"); int a = 100 + 200 / 10 - 3 * 10; printf("100 + 200 / 10 - 3 * 10 = %d\n", a); printf(" (100 + (200/10) - (3*10)) = 90\n"); int b = (100 + 200) / 10 - 3 * 10; printf("(100 + 200) / 10 - 3 * 10 = %d\n", b); printf(" ((300) / 10 - 30) = 0\n"); int c = 100 + 200 / (10 - 3) * 10; printf("100 + 200 / (10 - 3) * 10 = %d\n", c); printf(" (100 + (200 / 7) * 10) = 100 + 28*10 = 380\n"); int d = (100 + 200) / (10 - 3) * 10; printf("(100 + 200) / (10 - 3) * 10 = %d\n", d); printf(" ((300) / (7) * 10) = 42*10 = 420\n"); } /* Demonstrates common mistakes due to misunderstanding precedence */ void demonstrateCommonMistakes(void) { printf("\n=== Common Mistakes ===\n"); /* Mistake 1: No chaining comparison */ int a = 10, b = 20, c = 30; if (c > b > a) { printf("c > b > a is TRUE\n"); } else { printf("c > b > a is FALSE\n"); } printf(" ((c > b) > a) = (1 > 10) = 0\n"); /* Mistake 2: Assignment vs. comparison */ int x = 5; if (x = 10) { printf("x = 10 is TRUE (but assignment happened!)\n"); } printf(" Use x == 10 for comparison\n"); /* Mistake 3: Bitwise vs. logical */ int d = 1, e = 2; if (d & e) { printf("d & e is non-zero (bitwise AND)\n"); } if (d && e) { printf("d && e is TRUE (logical AND)\n"); } /* Mistake 4: Comma operator */ int y; y = 1, 2, 3; // Evaluated as (y = 1), 2, 3 printf("y = 1, 2, 3 => y = %d\n", y); printf(" ((y = 1), 2, 3) => y = 1\n"); }

Explanation

  1. demonstratePrecedence: Shows how different precedence levels affect evaluation.
  2. demonstrateAssociativity: Shows left-to-right and right-to-left evaluation.
  3. demonstrateParentheses: Shows how parentheses override default precedence.
  4. demonstrateCommonMistakes: Highlights typical errors like comparison chaining and assignment vs. equality.

Expected Output (partial)

text

=== Operator Precedence and Associativity === 1. Demonstrate Precedence 2. Demonstrate Associativity 3. Demonstrate Parentheses Effect 4. Demonstrate Common Mistakes 5. Exit Enter choice: 1 === Operator Precedence === 10 + 20 * 30 = 610 (20 * 30 evaluated first, then + 10) 5 << 2 > 10 = 1 ((5 << 2) > 10) = (20 > 10) = 1 5 > 3 == 1 = 1 ((5 > 3) == 1) = (1 == 1) = 1 5 & 3 == 3 = 1 (5 & (3 == 3)) = (5 & 1) = 1 Note: == has higher precedence than & (5 & 3) == 3 = 0 ((5 & 3) == 3) = (1 == 3) = 0 1 || 0 && 0 = 1 (1 || (0 && 0)) = (1 || 0) = 1

Common Pitfalls, Edge Cases & Memory Considerations

1. Comparison Chaining Does Not Work

In Python, a < b < c works as expected. In C, it evaluates as (a < b) < c.

c

int a = 10, b = 20, c = 30; if (a < b < c) { // (10 < 20) < 30 = 1 < 30 = 1 (true) } if (c > b > a) { // (30 > 20) > 10 = 1 > 10 = 0 (false) }

Solution: Use logical operators: if (a < b && b < c).

2. Assignment vs. Equality

Using = instead of == in conditions is a classic bug.

c

if (a = 10) { // Assigns 10 to a, always true }

Solution: Use == for comparison. Enable compiler warnings (-Wall).

3. Bitwise vs. Logical Operators

& has lower precedence than ==, causing unexpected grouping.

c

if (a & b == 0) { // Parsed as a & (b == 0) }

Solution: Use parentheses: if ((a & b) == 0).

4. Postfix vs. Prefix Increment

Postfix ++ has higher precedence than prefix ++, but mixing them on the same variable is undefined.

c

int a = 5; int result = a++ + ++a; // Undefined behavior

Solution: Avoid multiple side effects on the same variable in one expression.

5. Comma Operator Precedence

The comma operator has the lowest precedence. It can cause unexpected results.

c

int a = 1, 2, 3; // a = 1, then 2 and 3 discarded

Solution: Use parentheses to group comma expressions: int a = (1, 2, 3);

6. Ternary Operator Associativity

The ternary operator is right-associative, which can lead to surprising results.

c

int result = a > b ? 10 : c > d ? 20 : 30; // Parsed as: a > b ? 10 : (c > d ? 20 : 30)

Solution: Use parentheses to clarify nesting.

7. Shift Operator Precedence

Shift operators have lower precedence than arithmetic operators, which can cause unexpected results.

c

int result = 1 << 2 + 3; // Parsed as 1 << (2 + 3) = 1 << 5 = 32

Solution: Use parentheses: (1 << 2) + 3.

8. Function Call Order is Unspecified

Precedence does not determine the order in which function arguments are evaluated.

c

int result = f1() + f2() * f3(); // f1(), f2(), f3() may be called in any order

Solution: Avoid side effects in function arguments that depend on evaluation order.

9. sizeof and Side Effects

sizeof does not evaluate its operand (except for variable-length arrays in C99).

c

int a = 5; size_t s = sizeof(a++); // a is NOT incremented printf("%d\n", a); // 5

Solution: Do not rely on side effects inside sizeof.

10. Memory Considerations

  • Precedence and associativity do not affect memory directly.
  • Misunderstanding precedence can lead to logic errors, not memory errors.
  • Using parentheses improves readability without any performance cost.

Summary & Best Practices

Operator precedence and associativity are fundamental rules in C that determine how expressions are evaluated. Mastering them is essential for writing correct and predictable code.

Key Takeaways

  • Precedence determines which operator is evaluated first.
  • Associativity determines evaluation order for operators of the same precedence.
  • Parentheses have the highest precedence and can override default ordering.
  • Precedence and associativity determine grouping, not operand evaluation order.
  • Common mistakes include comparison chaining, assignment vs. equality, and bitwise vs. logical confusion.

Best Practices

  1. Use parentheses liberally to clarify intent, even when not strictly required.
  2. Avoid side effects (like ++) on the same variable in complex expressions.
  3. Use == for comparison and = for assignment; enable compiler warnings.
  4. Do not chain comparison operators; use && or ||.
  5. Parenthesize bitwise operations when mixed with relational/equality operators.
  6. Be cautious with the comma operator; use it only where intended (e.g., in for loops).
  7. Use explicit grouping in ternary operator expressions.
  8. Remember that sizeof does not evaluate its operand.
  9. Test expressions with different precedence to verify understanding.
  10. Document non-obvious expressions with comments explaining the evaluation order.

By mastering operator precedence and associativity, you’ll write C code that is both correct and easy to understand, avoiding subtle bugs that stem from misinterpreting how expressions are evaluated.

Learn C operators: arithmetic, relational, logical, bitwise, assignment, and special operators. Master precedence, associativity, and common pitfalls.

Operators in C: Arithmetic, Bitwise & More

Operators in C: A Comprehensive Tutorial for Programmers

Operators in C are special symbols that perform operations on operands (variables, constants, or expressions). They form the backbone of programming logic, enabling arithmetic calculations, comparisons, logical decisions, bit-level manipulations, and memory access. Understanding operators is essential for writing efficient and correct C programs.

This tutorial provides an in-depth exploration of C operators: their types, syntax, precedence, associativity, practical applications, common pitfalls, and best practices.


Conceptual Introduction & Fundamentals

What are Operators?

An operator is a symbol that tells the compiler to perform a specific mathematical, logical, or bitwise operation. Operators work on operands—the values or variables they operate on.

Example:

c

int sum = 10 + 20; // '+' is the operator, 10 and 20 are operands

Classification of Operators

C operators can be classified based on:

  1. Number of operands:
    • Unary: One operand (e.g., -a, ++a, !flag)
    • Binary: Two operands (e.g., a + b, a == b, a & b)
    • Ternary: Three operands (only the conditional operator ?:)
  2. Functionality:
    • Arithmetic operators
    • Relational operators
    • Logical operators
    • Bitwise operators
    • Assignment operators
    • Special operators (sizeof, comma, conditional, member, cast, address/dereference)

Operator Precedence and Associativity

Precedence determines the order in which operators are evaluated in an expression. Operators with higher precedence are evaluated first. Associativity determines the order when operators have the same precedence (left-to-right or right-to-left).

Key precedence levels (highest to lowest):

PrecedenceOperatorsAssociativity
1 (highest)(), [], ., ->, postfix ++/--Left to right
2Unary +, -, !, ~, *, &, sizeof, prefix ++/--, (type)Right to left
3*, /, %Left to right
4+, - (binary)Left to right
5<<, >>Left to right
6<, <=, >, >=Left to right
7==, !=Left to right
8& (bitwise AND)Left to right
9^ (bitwise XOR)Left to right
10| (bitwise OR)Left to right
11&&Left to right
12||Left to right
13?:Right to left
14=, +=, -=, etc.Right to left
15 (lowest), (comma)Left to right

Syntax & Core Mechanics

1. Arithmetic Operators

Arithmetic operators perform mathematical calculations.

OperatorDescriptionExample
+Additiona + b
-Subtractiona - b
*Multiplicationa * b
/Divisiona / b
%Modulus (remainder)a % b
+ (unary)Positive+a
- (unary)Negative-a
++Incrementa++, ++a
--Decrementa--, --a

Example:

c

#include <stdio.h> int main(void) { int a = 25, b = 5; printf("a + b = %d\n", a + b); // 30 printf("a - b = %d\n", a - b); // 20 printf("a * b = %d\n", a * b); // 125 printf("a / b = %d\n", a / b); // 5 (integer division) printf("a %% b = %d\n", a % b); // 0 printf("+a = %d\n", +a); // 25 printf("-a = %d\n", -a); // -25 /* Increment and decrement */ printf("a++ = %d\n", a++); // 25 (prints, then increments) printf("a = %d\n", a); // 26 printf("++a = %d\n", ++a); // 27 (increments, then prints) printf("a-- = %d\n", a--); // 27 (prints, then decrements) printf("a = %d\n", a); // 26 printf("--a = %d\n", --a); // 25 (decrements, then prints) return 0; }

Expected output:

text

a + b = 30 a - b = 20 a * b = 125 a / b = 5 a % b = 0 +a = 25 -a = -25 a++ = 25 a = 26 ++a = 27 a-- = 27 a = 26 --a = 25

Important: Integer division truncates the result (5 / 2 = 2, not 2.5).

2. Relational Operators

Relational operators compare two values and return 1 (true) or 0 (false).

OperatorDescriptionExample
<Less thana < b
>Greater thana > b
<=Less than or equala <= b
>=Greater than or equala >= b
==Equal toa == b
!=Not equal toa != b

Example:

c

#include <stdio.h> int main(void) { int a = 25, b = 5; printf("a < b : %d\n", a < b); // 0 (false) printf("a > b : %d\n", a > b); // 1 (true) printf("a <= b: %d\n", a <= b); // 0 printf("a >= b: %d\n", a >= b); // 1 printf("a == b: %d\n", a == b); // 0 printf("a != b : %d\n", a != b); // 1 return 0; }

Expected output:

text

a < b : 0 a > b : 1 a <= b: 0 a >= b: 1 a == b: 0 a != b : 1

3. Logical Operators

Logical operators combine or negate conditions.

OperatorDescriptionExample
&&Logical ANDa && b
||Logical ORa || b
!Logical NOT!a

Truth tables:

  • &&: true only if both true.
  • ||: true if at least one true.
  • !: reverses the truth value.

Example:

c

#include <stdio.h> int main(void) { int a = 25, b = 5; printf("a && b : %d\n", a && b); // 1 (both non-zero) printf("a || b : %d\n", a || b); // 1 printf("!a: %d\n", !a); // 0 (a is non-zero) /* Short-circuit evaluation */ if (a > 10 && b > 0) { printf("Both conditions true\n"); } if (a > 100 || b > 0) { printf("At least one condition true\n"); } return 0; }

Expected output:

text

a && b : 1 a || b : 1 !a: 0 Both conditions true At least one condition true

4. Bitwise Operators

Bitwise operators manipulate individual bits of integers.

OperatorDescriptionExample
&Bitwise ANDa & b
|Bitwise ORa | b
^Bitwise XORa ^ b
~Bitwise NOT (one’s complement)~a
<<Left shifta << b
>>Right shifta >> b

Example:

c

#include <stdio.h> int main(void) { int a = 25; // binary: 0001 1001 int b = 5; // binary: 0000 0101 printf("a & b = %d\n", a & b); // 1 (0000 0001) printf("a | b = %d\n", a | b); // 29 (0001 1101) printf("a ^ b = %d\n", a ^ b); // 28 (0001 1100) printf("~a = %d\n", ~a); // -26 (1110 0110 in two's complement) printf("a << 2 = %d\n", a << 2); // 100 (multiply by 4) printf("a >> 2 = %d\n", a >> 2); // 6 (divide by 4) return 0; }

Expected output:

text

a & b = 1 a | b = 29 a ^ b = 28 ~a = -26 a << 2 = 100 a >> 2 = 6

5. Assignment Operators

Assignment operators assign values to variables.

OperatorDescriptionEquivalent
=Simple assignmenta = b
+=Add and assigna = a + b
-=Subtract and assigna = a - b
*=Multiply and assigna = a * b
/=Divide and assigna = a / b
%=Modulus and assigna = a % b
&=Bitwise AND and assigna = a & b
|=Bitwise OR and assigna = a | b
^=Bitwise XOR and assigna = a ^ b
<<=Left shift and assigna = a << b
>>=Right shift and assigna = a >> b

Example:

c

#include <stdio.h> int main(void) { int a = 10; a += 5; // a = 15 printf("a += 5: %d\n", a); a -= 3; // a = 12 printf("a -= 3: %d\n", a); a *= 2; // a = 24 printf("a *= 2: %d\n", a); a /= 4; // a = 6 printf("a /= 4: %d\n", a); a %= 4; // a = 2 printf("a %%= 4: %d\n", a); a <<= 3; // a = 16 printf("a <<= 3: %d\n", a); a >>= 2; // a = 4 printf("a >>= 2: %d\n", a); a &= 6; // a = 4 (0100 & 0110 = 0100) printf("a &= 6: %d\n", a); a |= 3; // a = 7 (0100 | 0011 = 0111) printf("a |= 3: %d\n", a); a ^= 1; // a = 6 (0111 ^ 0001 = 0110) printf("a ^= 1: %d\n", a); return 0; }

Expected output:

text

a += 5: 15 a -= 3: 12 a *= 2: 24 a /= 4: 6 a %= 4: 2 a <<= 3: 16 a >>= 2: 4 a &= 6: 4 a |= 3: 7 a ^= 1: 6

6. Special Operators

sizeof Operator

Returns the size of a type or expression in bytes. Compile-time operator.

c

int num = 10; printf("sizeof(num) = %zu bytes\n", sizeof(num)); printf("sizeof(int) = %zu bytes\n", sizeof(int));

Comma Operator (,)

Evaluates both operands, returns the right operand’s value.

c

int x = (1, 2, 3); // x = 3

Conditional Operator (?:)

The only ternary operator in C.

c

int max = (a > b) ? a : b;

Member Operators (. and ->)

Access members of structures/unions.

c

struct Point p = {10, 20}; struct Point *ptr = &p; printf("%d\n", p.x); // dot operator printf("%d\n", ptr->y); // arrow operator

Cast Operator (type)

Converts one data type to another.

c

int x = (int)3.99; // x = 3

Address-of (&) and Dereference (*) Operators

c

int num = 42; int *ptr = &num; // & gets address printf("%d\n", *ptr); // * dereferences

Full Practical Code Example with Explanation

This program demonstrates all categories of operators in a realistic scenario: a simple calculator that performs arithmetic, bitwise, logical, and relational operations.

Program: Advanced Calculator

c

/** * advanced_calculator.c * Demonstrates all operator types in C. * Performs arithmetic, bitwise, relational, and logical operations. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <stdbool.h> #define MAX_LINE_LEN 100 /* Function prototypes */ int readInteger(const char *prompt, int *outValue); void demonstrateArithmetic(int a, int b); void demonstrateRelational(int a, int b); void demonstrateLogical(int a, int b); void demonstrateBitwise(int a, int b); void demonstrateAssignment(int a, int b); void demonstrateSpecial(int a); int main(void) { int a, b; int choice; char buffer[MAX_LINE_LEN]; printf("=== Advanced Calculator ===\n"); /* Read two numbers */ if (readInteger("Enter first number: ", &a) != 0) { fprintf(stderr, "Invalid input for first number.\n"); return EXIT_FAILURE; } if (readInteger("Enter second number: ", &b) != 0) { fprintf(stderr, "Invalid input for second number.\n"); return EXIT_FAILURE; } /* Menu-driven demonstration */ while (1) { printf("\n--- Operations Menu ---\n"); printf("1. Arithmetic Operations\n"); printf("2. Relational Operations\n"); printf("3. Logical Operations\n"); printf("4. Bitwise Operations\n"); printf("5. Assignment Operations\n"); printf("6. Special Operators\n"); printf("7. Exit\n"); printf("Enter choice: "); fflush(stdout); if (fgets(buffer, sizeof(buffer), stdin) == NULL) { fprintf(stderr, "Error reading choice.\n"); return EXIT_FAILURE; } choice = atoi(buffer); switch (choice) { case 1: demonstrateArithmetic(a, b); break; case 2: demonstrateRelational(a, b); break; case 3: demonstrateLogical(a, b); break; case 4: demonstrateBitwise(a, b); break; case 5: demonstrateAssignment(a, b); break; case 6: demonstrateSpecial(a); break; case 7: printf("Exiting.\n"); return EXIT_SUCCESS; default: printf("Invalid choice. Please try again.\n"); break; } } return EXIT_SUCCESS; } /* Demonstrates arithmetic operators */ void demonstrateArithmetic(int a, int b) { printf("\n=== Arithmetic Operators ===\n"); printf("%d + %d = %d\n", a, b, a + b); printf("%d - %d = %d\n", a, b, a - b); printf("%d * %d = %d\n", a, b, a * b); if (b != 0) { printf("%d / %d = %d\n", a, b, a / b); printf("%d %% %d = %d\n", a, b, a % b); } else { printf("Division and modulus by zero are undefined.\n"); } printf("+%d = %d\n", a, +a); printf("-%d = %d\n", a, -a); int temp = a; printf("%d++ = %d\n", temp, temp++); printf("Now temp = %d\n", temp); temp = a; printf("++%d = %d\n", temp, ++temp); printf("Now temp = %d\n", temp); } /* Demonstrates relational operators */ void demonstrateRelational(int a, int b) { printf("\n=== Relational Operators ===\n"); printf("%d < %d : %d\n", a, b, a < b); printf("%d > %d : %d\n", a, b, a > b); printf("%d <= %d: %d\n", a, b, a <= b); printf("%d >= %d: %d\n", a, b, a >= b); printf("%d == %d: %d\n", a, b, a == b); printf("%d != %d: %d\n", a, b, a != b); } /* Demonstrates logical operators */ void demonstrateLogical(int a, int b) { printf("\n=== Logical Operators ===\n"); printf("(%d && %d) : %d\n", a, b, a && b); printf("(%d || %d) : %d\n", a, b, a || b); printf("!%d: %d\n", a, !a); printf("!%d: %d\n", b, !b); /* Short-circuit evaluation */ if (a > 10 && b > 0) { printf("Both conditions true (a > 10 && b > 0)\n"); } else { printf("At least one condition false (a > 10 && b > 0)\n"); } if (a > 100 || b > 0) { printf("At least one condition true (a > 100 || b > 0)\n"); } } /* Demonstrates bitwise operators */ void demonstrateBitwise(int a, int b) { printf("\n=== Bitwise Operators ===\n"); printf("Binary of %d: ", a); for (int i = 31; i >= 0; i--) { printf("%d", (a >> i) & 1); } printf("\n"); printf("Binary of %d: ", b); for (int i = 31; i >= 0; i--) { printf("%d", (b >> i) & 1); } printf("\n"); printf("%d & %d = %d\n", a, b, a & b); printf("%d | %d = %d\n", a, b, a | b); printf("%d ^ %d = %d\n", a, b, a ^ b); printf("~%d = %d\n", a, ~a); printf("%d << 2 = %d\n", a, a << 2); printf("%d >> 2 = %d\n", a, a >> 2); } /* Demonstrates assignment operators */ void demonstrateAssignment(int a, int b) { printf("\n=== Assignment Operators ===\n"); int result = a; printf("result = %d\n", result); result += b; printf("result += %d: %d\n", b, result); result -= b; printf("result -= %d: %d\n", b, result); result *= b; printf("result *= %d: %d\n", b, result); if (b != 0) { result /= b; printf("result /= %d: %d\n", b, result); } result %= b; printf("result %%= %d: %d\n", b, result); result <<= 2; printf("result <<= 2: %d\n", result); result >>= 1; printf("result >>= 1: %d\n", result); result &= b; printf("result &= %d: %d\n", b, result); result |= b; printf("result |= %d: %d\n", b, result); result ^= b; printf("result ^= %d: %d\n", b, result); } /* Demonstrates special operators */ void demonstrateSpecial(int a) { printf("\n=== Special Operators ===\n"); /* sizeof */ printf("sizeof(%d) = %zu bytes\n", a, sizeof(a)); printf("sizeof(int) = %zu bytes\n", sizeof(int)); /* Address-of and dereference */ int *ptr = &a; printf("Address of a = %p\n", (void*)&a); printf("Value through pointer = %d\n", *ptr); /* Conditional operator */ int result = (a > 10) ? 100 : 200; printf("(a > 10) ? 100 : 200 = %d\n", result); /* Comma operator */ int x = (1, 2, 3); printf("Comma operator (1, 2, 3) = %d\n", x); /* Cast operator */ float f = 3.99f; int casted = (int)f; printf("(int)3.99 = %d\n", casted); } /* Reads an integer safely using fgets and strtol */ int readInteger(const char *prompt, int *outValue) { char buffer[MAX_LINE_LEN]; char *endPtr; long val; while (1) { printf("%s", prompt); fflush(stdout); if (fgets(buffer, sizeof(buffer), stdin) == NULL) { return -1; } errno = 0; val = strtol(buffer, &endPtr, 10); if (endPtr == buffer || (*endPtr != '\n' && *endPtr != '\0')) { printf("Invalid integer. Please try again.\n"); continue; } if (errno == ERANGE || val < -2147483647 - 1 || val > 2147483647) { printf("Number out of range. Please try again.\n"); continue; } *outValue = (int)val; return 0; } }

Explanation of Operator Usage

  • Arithmetic: Addition, subtraction, multiplication, division, modulus, unary plus/minus, increment/decrement.
  • Relational: All six comparison operators return 0 or 1.
  • Logical: &&, ||, ! with short-circuit behavior.
  • Bitwise: &, |, ^, ~, <<, >> with binary representation display.
  • Assignment: Simple and compound assignment operators.
  • Special: sizeof, &, *, ?:, comma, cast.

Expected Output (sample run)

text

=== Advanced Calculator === Enter first number: 25 Enter second number: 5 --- Operations Menu --- 1. Arithmetic Operations 2. Relational Operations 3. Logical Operations 4. Bitwise Operations 5. Assignment Operations 6. Special Operators 7. Exit Enter choice: 1 === Arithmetic Operators === 25 + 5 = 30 25 - 5 = 20 25 * 5 = 125 25 / 5 = 5 25 % 5 = 0 +25 = 25 -25 = -25 25++ = 25 Now temp = 26 ++25 = 26 Now temp = 26

Common Pitfalls, Edge Cases & Memory Considerations

1. Integer Division Truncation

5 / 2 evaluates to 2, not 2.5. This is a common source of bugs.

c

int a = 5, b = 2; double result = a / b; // 2.0, not 2.5

Solution: Cast one operand: (double)a / b.

2. Modulus with Negative Numbers

The result of % with negative operands is implementation-defined in C89, but defined in C99 (result has the sign of the dividend).

c

printf("%d\n", -5 % 2); // -1 (C99) printf("%d\n", 5 % -2); // 1 (C99)

Solution: Avoid modulus with negative numbers, or document the expected behavior.

3. Division by Zero

Division or modulus by zero is undefined behavior for integers and may cause a crash.

c

int a = 10, b = 0; int c = a / b; // Undefined behavior

Solution: Always check b != 0 before division/modulus.

4. Overflow in Arithmetic

Arithmetic can overflow, especially with signed integers (undefined behavior).

c

int max = INT_MAX; int result = max + 1; // Undefined behavior

Solution: Check ranges before operations: if (a > INT_MAX - b).

5. Bitwise Operator Precedence

Bitwise operators have lower precedence than relational operators, causing unexpected results.

c

if (a & b == 0) { // Parsed as a & (b == 0), not (a & b) == 0 }

Solution: Use parentheses: if ((a & b) == 0).

6. Logical vs. Bitwise Operators

&& and || are logical; & and | are bitwise. They are not interchangeable.

c

if (a & 0x1) { // Bitwise: checks if bit 0 is set } if (a && 0x1) { // Logical: checks if both are non-zero (always true for 0x1) }

7. Assignment vs. Comparison

Using = instead of == in conditions is a classic bug.

c

if (a = 10) { // Assigns 10 to a, condition always true } if (a == 10) { // Correct comparison }

Solution: Enable compiler warnings (-Wall) to catch this.

8. Side Effects in Expressions

Multiple side effects on the same variable in one expression are undefined.

c

int a = 5; int result = a++ + ++a; // Undefined behavior

Solution: Avoid multiple side effects on the same variable in one expression.

9. Shift Operator with Negative or Excessive Amounts

Shifting by a negative amount or by more than the number of bits is undefined behavior.

c

int x = 1; int y = x << 40; // Undefined if int is 32 bits int z = x << -1; // Undefined

Solution: Ensure shift amounts are between 0 and sizeof(int)*8 - 1.

10. Memory Considerations

  • Bitwise operators are fast and use minimal memory.
  • Compound assignment operators may generate more efficient code.
  • Using sizeof is compile-time; no runtime overhead.
  • Cast operators do not allocate memory; they reinterpret values.

Summary & Best Practices

Operators in C are the building blocks of expressions. Mastering them is essential for writing efficient, correct, and maintainable code.

Key Takeaways

  • C provides arithmetic, relational, logical, bitwise, assignment, and special operators.
  • Operators can be unary, binary, or ternary.
  • Precedence and associativity determine evaluation order.
  • Integer division truncates; modulus works with integers only.
  • Bitwise operators work at the bit level for low-level programming.
  • Compound assignment operators improve readability and efficiency.

Best Practices

  1. Use parentheses to clarify precedence, even when not strictly required.
  2. Check for division by zero before performing division or modulus.
  3. Avoid side effects in complex expressions; keep expressions simple.
  4. Use compound assignment operators (+=, <<=) for concise code.
  5. Prefer bitwise operators for flags and masks in low-level code.
  6. Enable compiler warnings (-Wall -Wextra) to catch common mistakes.
  7. Use relational operators with parentheses in complex conditions.
  8. Be aware of integer overflow; use larger types or check ranges.
  9. Use sizeof instead of hardcoding sizes.
  10. Document non-obvious bitwise or ternary operations with comments.

By mastering operators, you’ll write C programs that are both efficient and correct, from simple calculations to complex bit manipulations.