Chapter 3: Advanced Control Structures and Data Types - Switch, Pointers, Structs, and Variable Scope

Project Requirements

This chapter introduces advanced control structures and data types in C programming, including switch statements, pointers, structures, and variable scope rules.

Project Analysis

  1. Learn switch statements for multi-way conditional branching
  2. Understand pointers and memory addresses
  3. Use structures to group related data
  4. Explore variable scope and lifetime

Task 1: Switch Statements - Multi-way Branching

I. Task Presentation

Learn how to use switch statements for efficient multi-way conditional branching, often preferred over multiple if-else statements.

II. Specific Implementation

From the vending machine example, it can be analyzed that:

  1. Switch statements provide a clean way to handle multiple possible values
  2. Each case handles a specific value
  3. break statements prevent fall-through to the next case
  4. default case handles any unmatched values
#include <stdio.h>

int main() {
    int choice;

    printf("Please select an item:\n");
    printf("  1. Coke\n");
    printf("  2. Sprite\n");
    printf("  3. Orange Juice\n");
    printf("  4. Water\n");
    printf("Enter your choice (1-4): ");
    scanf("%d", &choice);

    // Execute different actions based on the user's choice
    switch (choice) {
        case 1:
            printf("A can of Coke is dispensed.\n");
            break;

        case 2:
            printf("A can of Sprite is dispensed.\n");
            break;

        case 3:
            printf("A bottle of Orange Juice is dispensed.\n");
            break;

        case 4:
            printf("A bottle of Water is dispensed.\n");
            break;

        default:
            printf("Invalid selection! Please choose 1-4.\n");
            break;
    }

    return 0;
}
    

III. Relevant Knowledge

(I) Switch Statement Syntax and Rules

The switch statement evaluates an expression and matches it to case labels.

switch (expression) {
    case constant1:
        // statements to execute if expression equals constant1
        break;
    case constant2:
        // statements to execute if expression equals constant2
        break;
    // more cases...
    default:
        // statements to execute if expression doesn't match any case
        break;
}
    

Important Points:

(II) Fall-through Behavior and Break Statements

By default, once a matching case is found, all subsequent cases are executed until a break statement or the end of the switch block.

Example without breaks (fall-through):

switch (grade) {
    case 'A':
        printf("Excellent! ");
    case 'B':
        printf("Good! ");
    case 'C':
        printf("Average. ");
    case 'D':
        printf("Below average. ");
    case 'F':
        printf("Failed.\n");
}
    

If grade is 'B', the output would be: "Good! Average. Below average. Failed."

Example with proper breaks:

switch (grade) {
    case 'A':
        printf("Excellent!\n");
        break;
    case 'B':
        printf("Good!\n");
        break;
    case 'C':
        printf("Average.\n");
        break;
    case 'D':
        printf("Below average.\n");
        break;
    case 'F':
        printf("Failed.\n");
        break;
    default:
        printf("Invalid grade.\n");
        break;
}
        

Task 2: Pointers - Understanding Memory Addresses

I. Task Presentation

Learn how to work with pointers - variables that store memory addresses rather than values.

II. Specific Implementation

From the pointer example, it can be analyzed that:

  1. Every variable has a memory address
  2. Pointers store memory addresses
  3. Pointer variables must be declared with an asterisk (*)
  4. Address-of operator (&) gives the memory address of a variable
  5. Dereference operator (*) accesses the value at a pointer's address
#include <stdio.h>

int main() {
    // Declare a regular integer variable
    int my_number = 42;

    // Declare a pointer variable that can store the address of an integer
    int *ptr_to_number;

    // Assign the memory address of 'my_number' to 'ptr_to_number'
    ptr_to_number = &my_number;

    printf("Value of my_number: %d\n", my_number);
    printf("Address of my_number: %p\n", &my_number);
    printf("Value stored in ptr_to_number: %p\n", ptr_to_number);
    printf("Value pointed to by ptr_to_number: %d\n", *ptr_to_number);

    // Change the value through the pointer
    *ptr_to_number = 100;
    printf("After changing through pointer, my_number is: %d\n", my_number);

    return 0;
}
    

III. Relevant Knowledge

(I) Pointer Declaration and Assignment

Pointers are special variables that store memory addresses.

1. Declaring Pointers

// Syntax: data_type *pointer_name;
int *ptr;          // declares a pointer to an integer
float *fptr;       // declares a pointer to a float
char *cptr;        // declares a pointer to a character
    

2. Assigning Values to Pointers

int value = 10;
int *ptr;

ptr = &value;      // Assign address of 'value' to 'ptr'
// Now ptr contains the address of 'value'
    

3. Dereferencing Pointers

int value = 10;
int *ptr = &value;

printf("%d", *ptr);  // Outputs the value at ptr's address (10)
*ptr = 20;           // Changes the value at ptr's address
printf("%d", value); // Outputs 20 (value was changed)
    

(II) Pointer Arithmetic and Common Use Cases

Example: Pointer Arithmetic with Arrays

#include <stdio.h>

int main() {
    int numbers[5] = {10, 20, 30, 40, 50};
    int *ptr = numbers;  // Points to the first element of the array

    printf("Using array notation: %d\n", numbers[0]);
    printf("Using pointer: %d\n", *ptr);  // Same as numbers[0]

    // Moving the pointer to the next element
    ptr++;  // Now points to numbers[1]
    printf("Second element: %d\n", *ptr);  // Same as numbers[1]

    // Accessing array elements using pointer arithmetic
    for(int i = 0; i < 5; i++) {
        printf("Element %d: %d\n", i, *(numbers + i));
    }

    return 0;
}
    

Task 3: Pointers and Functions - Call by Reference

I. Task Presentation

Learn how to pass variables to functions by reference using pointers, allowing functions to modify the original variables.

II. Specific Implementation

Comparison between pass-by-value and pass-by-pointer:

  1. Pass-by-value: Function receives a copy of the variable's value
  2. Pass-by-pointer: Function receives the address of the original variable
  3. Pass-by-pointer allows functions to modify the original variables
#include <stdio.h>

// Function to swap two integers using pointers (pass-by-pointer)
void swap(int *ptr_a, int *ptr_b) {
    int temp = *ptr_a;  // Store value at address pointed to by ptr_a
    *ptr_a = *ptr_b;    // Copy value at address pointed to by ptr_b to address pointed to by ptr_a
    *ptr_b = temp;      // Copy temp value to address pointed to by ptr_b
}

int main() {
    int x = 10, y = 20;

    printf("Before swap: x = %d, y = %d\n", x, y);
    swap(&x, &y);  // Pass addresses of x and y
    printf("After swap: x = %d, y = %d\n", x, y);

    return 0;
}
    

III. Pointers and Function Limitations

Pass-by-Value vs Pass-by-Pointer

Pass-by-Value (Values are copied):

#include <stdio.h>

void increment_fail(int a) {
    a = a + 1;  // Only modifies the copy, not the original
}

int main() {
    int num = 5;
    printf("Before: %d\n", num);  // Output: 5
    increment_fail(num);
    printf("After: %d\n", num);   // Output: 5 (unchanged!)
    
    return 0;
}
    

Pass-by-Pointer (Original can be modified):

#include <stdio.h>

void increment_success(int *ptr) {
    *ptr = *ptr + 1;  // Modifies the value at the pointer's address
}

int main() {
    int num = 5;
    printf("Before: %d\n", num);    // Output: 5
    increment_success(&num);         // Pass address of num
    printf("After: %d\n", num);     // Output: 6 (changed!)
    
    return 0;
}
    

Task 4: Structures - Grouping Related Data

I. Task Presentation

Learn how to define and use structures to group different data types together into a single entity.

II. Specific Implementation

Structures allow us to create custom data types:

  1. A struct can contain multiple variables of different types
  2. Structs are defined with the struct keyword
  3. Members of structs are accessed with the dot operator (.)
  4. Structs can be used to model real-world entities
#include <stdio.h>
#include <string.h>  // For strcpy

// Define a structure for a student
struct Student {
    char name[50];
    int id;
    float gpa;
};

int main() {
    // Declare a variable of type struct Student
    struct Student student1;

    // Assign values to the struct members
    strcpy(student1.name, "Alice Johnson");
    student1.id = 12345;
    student1.gpa = 3.85;

    // Access and print struct members
    printf("Name: %s\n", student1.name);
    printf("ID: %d\n", student1.id);
    printf("GPA: %.2f\n", student1.gpa);

    return 0;
}
    

III. Relevant Knowledge

(I) Defining and Using Structures

Structures (structs) allow grouping of related variables of different types under a single name.

1. Defining a Structure

struct structure_name {
    data_type member1;
    data_type member2;
    // ... more members
};
    

2. Declaring Struct Variables

struct Student {
    char name[50];
    int id;
    float gpa;
};

struct Student s1, s2;      // Declare two Student variables
struct Student s3 = {"Bob", 67890, 3.50};  // Initialize with values
    

3. Accessing Structure Members

strcpy(s1.name, "John");  // Assign to name member
s1.id = 11111;           // Assign to id member
s1.gpa = 3.90;           // Assign to gpa member

printf("Name: %s", s1.name);  // Access member
    

(II) Arrays of Structures and Nested Structures

1. Arrays of Structures

#include <stdio.h>
#include <string.h>

struct Student {
    char name[50];
    int id;
    float gpa;
};

int main() {
    // Array of structures
    struct Student class[3] = {
        {"Alice", 101, 3.85},
        {"Bob", 102, 3.20},
        {"Charlie", 103, 3.95}
    };

    // Print all students
    for(int i = 0; i < 3; i++) {
        printf("Student %d: %s (ID: %d, GPA: %.2f)\n", 
               i+1, class[i].name, class[i].id, class[i].gpa);
    }

    return 0;
}
    

2. Nested Structures

struct Address {
    char street[100];
    char city[50];
    int zip;
};

struct Student {
    char name[50];
    struct Address addr;   // Nested structure
};
    

Task 5: Pointers to Structures

I. Task Presentation

Learn how to use pointers to access structure members efficiently, especially when passing structures to functions.

II. Specific Implementation

Two ways to access struct members via pointers:

  1. Using dereference operator and dot: (*pointer).member
  2. Using arrow operator: pointer->member (shorthand)
#include <stdio.h>
#include <string.h>

struct Student {
    char name[50];
    int id;
    float gpa;
};

// Function that takes a pointer to a struct
void printStudent(struct Student *s) {
    printf("Name: %s\n", s->name);  // Arrow operator
    printf("ID: %d\n", s->id);
    printf("GPA: %.2f\n", s->gpa);
}

int main() {
    struct Student student1 = {"Alice", 12345, 3.85};
    struct Student *ptr = &student1;

    // Access members using arrow operator
    printf("Using arrow operator:\n");
    printf("Name: %s\n", ptr->name);
    printf("ID: %d\n", ptr->id);
    printf("GPA: %.2f\n", ptr->gpa);

    // Pass pointer to function
    printf("\nIn function:\n");
    printStudent(ptr);

    return 0;
}
    

III. Structures in Action - Monster Battle Game

Putting structures and pointers together in a game scenario:

#include <stdio.h>
#include <string.h>

struct Monster {
    char name[50];
    int hp;
    int attack;
};

// Function to perform attack using pointers
void performAttack(struct Monster *attacker, struct Monster *target) {
    printf("%s attacks %s!\n", attacker->name, target->name);
    target->hp -= attacker->attack;
    
    if (target->hp < 0) target->hp = 0;
    
    printf("%s takes %d damage. Remaining HP: %d\n", 
           target->name, attacker->attack, target->hp);
           
    if (target->hp == 0) {
        printf("%s has been defeated!\n", target->name);
    }
}

int main() {
    struct Monster T1000 = {"Violent T1000", 80, 15};
    struct Monster T800 = {"T800 the Arnold", 100, 10};

    printf("Battle Start!\n");
    printf("T1000: HP=%d, Attack=%d\n", T1000.hp, T1000.attack);
    printf("T800: HP=%d, Attack=%d\n", T800.hp, T800.attack);

    // Battle rounds
    performAttack(&T1000, &T800);  // T1000 attacks T800
    performAttack(&T800, &T1000);  // T800 attacks T1000

    return 0;
}
    

Task 6: Variable Scope and Storage Classes

I. Task Presentation

Understand the scope and lifetime of variables in C, including local, global, and static variables.

II. Specific Implementation

Three types of variable scope:

  1. Local variables: Only accessible within the function/block
  2. Global variables: Accessible throughout the program
  3. Static variables: Retain their value between function calls
#include <stdio.h>

// Global variable - accessible everywhere
int global_var = 10;

void demo_function(int param) {
    // Local variable - only accessible in this function
    int local_var = 20;

    printf("Inside function:\n");
    printf("  global_var = %d\n", global_var);
    printf("  param = %d\n", param);
    printf("  local_var = %d\n", local_var);

    // Modifying global variable affects it everywhere
    global_var = 88;
}

int main() {
    int arg = 5;

    printf("Before function call:\n");
    printf("  global_var = %d\n", global_var);
    printf("  arg = %d\n", arg);

    demo_function(arg);

    printf("\nAfter function call:\n");
    printf("  global_var = %d\n", global_var);  // Changed by function
    printf("  arg = %d\n", arg);                // Unchanged (pass-by-value)

    return 0;
}
    

III. Static Variables and Storage Duration

Static variables retain their value between function calls:

#include <stdio.h>

// Function with a local variable
void counter_local() {
    int count = 0;  // This is reinitialized each time
    count++;
    printf("Local count: %d\n", count);
}

// Function with a static variable
void counter_static() {
    static int count = 0;  // Initialized only once
    count++;
    printf("Static count: %d\n", count);
}

int main() {
    printf("Calling counter_local three times:\n");
    for(int i = 0; i < 3; i++) {
        counter_local();
    }

    printf("\nCalling counter_static three times:\n");
    for(int i = 0; i < 3; i++) {
        counter_static();
    }

    return 0;
}
    

Output would be:

Calling counter_local three times:
Local count: 1
Local count: 1
Local count: 1

Calling counter_static three times:
Static count: 1
Static count: 2
Static count: 3
        

(I) Variable Storage Classes Summary

1. auto (default)

2. register

3. static

4. extern

Scope vs Lifetime:

Practice Exercise 1

【Exercise 1】Write a program that uses a switch statement to implement a simple calculator that can add, subtract, multiply, or divide two numbers.

Analysis: The calculator should take an operator as input and perform the corresponding operation on two numbers.

Solution:

#include <stdio.h>

int main() {
    char operator;
    double num1, num2, result;

    printf("Enter operator (+, -, *, /): ");
    scanf(" %c", &operator);
    printf("Enter two numbers: ");
    scanf("%lf %lf", &num1, &num2);

    switch(operator) {
        case '+':
            result = num1 + num2;
            printf("%.2lf + %.2lf = %.2lf\n", num1, num2, result);
            break;
        case '-':
            result = num1 - num2;
            printf("%.2lf - %.2lf = %.2lf\n", num1, num2, result);
            break;
        case '*':
            result = num1 * num2;
            printf("%.2lf * %.2lf = %.2lf\n", num1, num2, result);
            break;
        case '/':
            if(num2 != 0) {
                result = num1 / num2;
                printf("%.2lf / %.2lf = %.2lf\n", num1, num2, result);
            } else {
                printf("Error: Division by zero!\n");
            }
            break;
        default:
            printf("Error: Invalid operator!\n");
            break;
    }

    return 0;
        

Practice Exercise 2

【Exercise 2】Create a structure to represent a book with title, author, and year published. Also write a function that takes a pointer to the book structure and prints its details.

Analysis: Define a book structure and implement a function that accesses the structure through a pointer.

Solution:

#include <stdio.h>
#include <string.h>

struct Book {
    char title[100];
    char author[50];
    int year;
};

void printBook(struct Book *b) {
    printf("Title: %s\n", b->title);
    printf("Author: %s\n", b->author);
    printf("Year Published: %d\n", b->year);
}

int main() {
    struct Book book1 = {
        "The C Programming Language",
        "Brian Kernighan and Dennis Ritchie",
        1978
    };

    printf("Book Details:\n");
    printBook(&book1);

    return 0;
        

Practice Exercise 3

【Exercise 3】Write a function that swaps two integers using pointers, then demonstrate how it modifies the original values.

Analysis: Create a swap function that takes pointers to two integers and exchanges their values.

Solution:

#include <stdio.h>

void swap(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

int main() {
    int x = 10, y = 20;

    printf("Before swap: x = %d, y = %d\n", x, y);
    swap(&x, &y);
    printf("After swap: x = %d, y = %d\n", x, y);

    return 0;
        

This demonstrates the difference between pass-by-value and pass-by-pointer. The original variables x and y are modified because we're passing pointers to their memory addresses, rather than copies of their values.

Summary of Chapter 3

Key Concepts Learned:

  1. Switch Statements: Multi-way branching for efficient conditional logic
  2. Pointers: Variables that store memory addresses
  3. Pointer Arithmetic: Moving pointers to access array elements
  4. Pass-by-Pointer: Allowing functions to modify original variables
  5. Structures: Grouping related data of different types
  6. Structure Arrays: Organizing multiple related records
  7. Variable Scope: Local, global, and static variable lifetimes

Applications: