C Programming Basics: Part 3 – Operators and Expressions

Welcome to the third part of our C programming tutorial series! In our previous post, we explored variables and data types in C. Now, we’ll dive into operators and expressions, which allow us to manipulate data and perform calculations in our programs.

What Are Operators and Expressions?

In C, operators are symbols that tell the compiler to perform specific mathematical or logical operations. Expressions are combinations of variables, constants, and operators that evaluate to a single value.

Think of operators as verbs in a sentence, telling the computer what action to take with the data.

Arithmetic Operators

Arithmetic operators perform mathematical calculations:

OperatorDescriptionExample
+Additiona + b
-Subtractiona - b
*Multiplicationa * b
/Divisiona / b
%Modulus (remainder)a % b
++Incrementa++ or ++a
--Decrementa-- or --a

Example:

C
#include <stdio.h>

int main() {
    int a = 10, b = 3;
    
    printf("Addition: %d\n", a + b);         // 13
    printf("Subtraction: %d\n", a - b);      // 7
    printf("Multiplication: %d\n", a * b);   // 30
    printf("Division: %d\n", a / b);         // 3
    printf("Modulus: %d\n", a % b);          // 1
    
    int c = a++;
    printf("Post-increment: c = %d, a = %d\n", c, a);  // c = 10, a = 11
    
    int d = ++b;
    printf("Pre-increment: d = %d, b = %d\n", d, b);   // d = 4, b = 4
    
    return 0;
}

Integer Division vs. Floating-Point Division

Be careful with the division operator (/). When both operands are integers, C performs integer division, discarding any remainder:

C
int result = 5 / 2; // result = 2, not 2.5

To get a floating-point result, at least one operand must be a float:

C
float result = 5.0 / 2;  // result = 2.5
// or
float result = (float)5 / 2;  // result = 2.5

Increment and Decrement Operators

The increment (++) and decrement (--) operators are unique because they can be used in two ways:

  • Pre-increment/decrement: ++a or --a (increment/decrement first, then use the value)
  • Post-increment/decrement: a++ or a-- (use the value first, then increment/decrement)

Let’s visualize how these operators work:

sequenceDiagram
    participant Pre as Pre-increment (++a)
    participant Post as Post-increment (a++)
    
    Note over Pre: Initial value: a = 5
    Pre->>Pre: Increment first (a = 6)
    Pre->>Pre: Then use value (6)
    
    Note over Post: Initial value: a = 5
    Post->>Post: Use value first (5)
    Post->>Post: Then increment (a = 6)

Assignment Operators

Assignment operators assign values to variables:

OperatorDescriptionExampleEquivalent
=Basic assignmenta = ba = b
+=Add and assigna += ba = a + b
-=Subtract and assigna -= ba = a - b
*=Multiply and assigna *= ba = a * b
/=Divide and assigna /= ba = a / b
%=Modulus and assigna %= ba = a % b

Example:

C
#include <stdio.h>

int main() {
    int a = 10;
    
    a += 5;  // a = a + 5
    printf("After a += 5: %d\n", a);  // 15
    
    a -= 3;  // a = a - 3
    printf("After a -= 3: %d\n", a);  // 12
    
    a *= 2;  // a = a * 2
    printf("After a *= 2: %d\n", a);  // 24
    
    a /= 4;  // a = a / 4
    printf("After a /= 4: %d\n", a);  // 6
    
    a %= 4;  // a = a % 4
    printf("After a %%= 4: %d\n", a);  // 2
    
    return 0;
}

Comparison Operators

Comparison operators compare two values and return a boolean result (1 for true, 0 for false):

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

Example:

C
#include <stdio.h>

int main() {
    int a = 10, 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);    // 1 (true)
    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 (false)
    
    return 0;
}

Common Mistake: Confusing = and ==

A common error in C is using the assignment operator (=) when you mean to use the equality comparison operator (==):

C
if (a = b) {  // WRONG: This assigns b to a, then evaluates if a is non-zero
    // Code here
}

if (a == b) {  // CORRECT: This checks if a is equal to b
    // Code here
}

Logical Operators

Logical operators perform logical operations, typically on boolean expressions:

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

Example:

C
#include <stdio.h>

int main() {
    int a = 1, b = 0;  // In C, 1 is true, 0 is false
    
    printf("a && b: %d\n", a && b);  // 0 (false)
    printf("a || b: %d\n", a || b);  // 1 (true)
    printf("!a: %d\n", !a);          // 0 (false)
    printf("!b: %d\n", !b);          // 1 (true)
    
    return 0;
}

Logical Operator Behavior

Here’s a visualization of how logical operators evaluate:

graph LR
    subgraph "Logical AND"
        A1["true && true"] --> B1[true]
        A2["true && false"] --> B2[false]
        A3["false && true"] --> B3[false]
        A4["false && false"] --> B4[false]
    end
    
    subgraph "Logical OR"
        C1["true || true"] --> D1[true]
        C2["true || false"] --> D2[true]
        C3["false || true"] --> D3[true]
        C4["false || false"] --> D4[false]
    end
    
    subgraph "Logical NOT"
        E1["!true"] --> F1[false]
        E2["!false"] --> F2[true]
    end

Short-Circuit Evaluation

C uses short-circuit evaluation for logical operators, which means it stops evaluating as soon as the result is determined:

  • For &&: If the first operand is false, the second is not evaluated
  • For ||: If the first operand is true, the second is not evaluated

This can be useful for conditional checks:

C
// Check if pointer is not NULL before accessing its value
if (ptr != NULL && ptr->value > 0) {
    // Safe to use ptr
}

Bitwise Operators

Bitwise operators manipulate individual bits in integer values:

OperatorDescriptionExample
&Bitwise ANDa & b
|Bitwise ORa | b
^Bitwise XORa ^ b
~Bitwise NOT~a
<<Left shifta << n
>>Right shifta >> n

Example:

C
#include <stdio.h>

int main() {
    unsigned char a = 5;  // 00000101 in binary
    unsigned char b = 9;  // 00001001 in binary
    
    printf("a & b: %d\n", a & b);    // 1 (00000001)
    printf("a | b: %d\n", a | b);    // 13 (00001101)
    printf("a ^ b: %d\n", a ^ b);    // 12 (00001100)
    printf("~a: %d\n", ~a);          // 250 (11111010)
    printf("a << 1: %d\n", a << 1);  // 10 (00001010)
    printf("b >> 1: %d\n", b >> 1);  // 4 (00000100)
    
    return 0;
}

Visualizing Bitwise Operations

Let’s visualize how bitwise AND, OR, and XOR work:

Bitwise Operations Operand A (5) Operand B (9) Result AND (&) 00000101 00001001 00000001 OR (|) 00000101 00001001 00001101 XOR (^) 00000101 00001001 00001100 Left Shift (<<) 00000101 shift 1 00001010 Right Shift (>>) 00001001 shift 1 00000100

Bitwise operators are powerful for low-level operations like:

  • Setting, clearing, or toggling specific bits
  • Creating bit masks for flags or options
  • Efficient multiplication or division by powers of 2
  • Working with hardware registers

The Conditional (Ternary) Operator

The conditional operator (? :) is a shorthand for an if-else statement:

C
condition ? expression_if_true : expression_if_false

Example:

C
#include <stdio.h>

int main() {
    int a = 10, b = 5;
    int max = (a > b) ? a : b;
    
    printf("Maximum of %d and %d is %d\n", a, b, max);  // 10
    
    // Equivalent if-else statement:
    /*
    int max;
    if (a > b) {
        max = a;
    } else {
        max = b;
    }
    */
    
    return 0;
}

Other Important Operators

The Sizeof Operator

The sizeof operator returns the size (in bytes) of a variable or data type:

C
#include <stdio.h>

int main() {
    int a;
    double b;
    
    printf("Size of int: %zu bytes\n", sizeof(int));        // Typically 4
    printf("Size of double: %zu bytes\n", sizeof(double));  // Typically 8
    printf("Size of variable a: %zu bytes\n", sizeof(a));   // Same as sizeof(int)
    
    return 0;
}

Address and Dereference Operators

  • The address operator (&) returns the memory address of a variable
  • The dereference operator (*) accesses the value at a memory address
C
#include <stdio.h>

int main() {
    int a = 10;
    int *ptr = &a;  // ptr holds the address of a
    
    printf("Value of a: %d\n", a);            // 10
    printf("Address of a: %p\n", &a);         // Memory address (e.g., 0x7ffeeb1be2bc)
    printf("Value of ptr: %p\n", ptr);        // Same address as &a
    printf("Value pointed to by ptr: %d\n", *ptr);  // 10
    
    *ptr = 20;  // Change the value at the address stored in ptr
    printf("New value of a: %d\n", a);        // 20
    
    return 0;
}

Operator Precedence and Associativity

Not all operators are created equal! C has rules about which operators are evaluated first in an expression:

  1. Precedence: Determines which operator is evaluated first. Higher precedence operators are evaluated before lower precedence ones.
  2. Associativity: Determines the order of evaluation when operators have the same precedence (left-to-right or right-to-left).

Here’s a simplified precedence table (from highest to lowest):

PrecedenceOperatorDescriptionAssociativity
1() [] -> .Parentheses, array subscript, member accessLeft to right
2! ~ ++ -- + - * & sizeofUnary operatorsRight to left
3* / %Multiplication, division, modulusLeft to right
4+ -Addition, subtractionLeft to right
5<< >>Bit shiftLeft 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? :Conditional operatorRight to left
14= += -= etc.Assignment operatorsRight to left
15,Comma operatorLeft to right

Example:

C
#include <stdio.h>

int main() {
    int a = 5, b = 3, c = 8;
    int result;
    
    // Without parentheses, follows precedence rules
    result = a + b * c;  // * has higher precedence than +
    printf("a + b * c = %d\n", result);  // 5 + (3 * 8) = 5 + 24 = 29
    
    // With parentheses, we can override precedence
    result = (a + b) * c;
    printf("(a + b) * c = %d\n", result);  // (5 + 3) * 8 = 8 * 8 = 64
    
    return 0;
}

Using Parentheses for Clarity

When in doubt, use parentheses to make your intentions clear. Even if they’re not strictly necessary for correct evaluation, parentheses can make your code more readable:

C
// Hard to read at a glance
int result = a * b + c * d + e / f;

// Much clearer
int result = (a * b) + (c * d) + (e / f);

Expressions and Statements

Now that we understand operators, let’s clarify the difference between expressions and statements:

  • Expression: A combination of operators, constants, and variables that evaluates to a value
  • Statement: A complete instruction that performs some action

An expression becomes a statement when it’s followed by a semicolon:

C
// Expression
a + b

// Statement
a + b;  // This evaluates a + b but does nothing with the result

// Useful statement
c = a + b;  // The expression a + b is calculated and assigned to c

Practical Applications

Let’s see some practical examples of operators and expressions in action:

Temperature Conversion

C
#include <stdio.h>

int main() {
    float celsius, fahrenheit;
    
    printf("Enter temperature in Celsius: ");
    scanf("%f", &celsius);
    
    // Formula using operators: F = (C * 9/5) + 32
    fahrenheit = (celsius * 9.0/5.0) + 32.0;
    
    printf("%.2f°C is %.2f°F\n", celsius, fahrenheit);
    
    return 0;
}

Calculating a Bill with Tax and Tip

C
#include <stdio.h>

int main() {
    float bill_amount, tax_rate, tip_percentage;
    
    printf("Enter bill amount: $");
    scanf("%f", &bill_amount);
    
    printf("Enter tax rate (%%): ");
    scanf("%f", &tax_rate);
    
    printf("Enter tip percentage (%%): ");
    scanf("%f", &tip_percentage);
    
    // Calculate tax amount
    float tax_amount = bill_amount * (tax_rate / 100);
    
    // Calculate subtotal (bill + tax)
    float subtotal = bill_amount + tax_amount;
    
    // Calculate tip amount based on subtotal
    float tip_amount = subtotal * (tip_percentage / 100);
    
    // Calculate total bill
    float total = subtotal + tip_amount;
    
    printf("\n--- Bill Summary ---\n");
    printf("Original bill: $%.2f\n", bill_amount);
    printf("Tax (%.2f%%): $%.2f\n", tax_rate, tax_amount);
    printf("Subtotal: $%.2f\n", subtotal);
    printf("Tip (%.2f%%): $%.2f\n", tip_percentage, tip_amount);
    printf("Total: $%.2f\n", total);
    
    return 0;
}

Conclusion

Operators and expressions are the workhorses of C programming, allowing you to manipulate data in countless ways. From simple arithmetic to complex bitwise operations, understanding how operators work is essential for becoming proficient in C.

Remember these key points:

  • Understand operator precedence and use parentheses when in doubt
  • Be careful with integer division
  • Watch out for common mistakes like confusing = and ==
  • Use short-circuit evaluation to your advantage
  • Choose the appropriate operators for your task to make code efficient and readable

In the next part of our series, we’ll explore control flow in C, including conditional statements and loops that allow your programs to make decisions and repeat actions.

Practice Exercises

  1. Write a program that takes three integers as input and outputs the largest value using only the conditional operator.
  2. Create a program that converts a decimal number to its binary representation using bitwise operators.
  3. Write a program that swaps the values of two variables without using a temporary variable. (Hint: Use arithmetic or XOR operations)
  4. Create a program that determines if an integer is even or odd, positive or negative, and a multiple of 5 or not.
  5. Write a program that calculates the area and perimeter of a rectangle using variables, arithmetic operators, and formatted output.

Happy coding!

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top