CACS151 C Programming

C ProgrammingUnit 810 min read

Pointers, Memory Management & Dynamic Allocation in C

Unit 8 of C Programming covers pointers (addresses, dereferencing, arithmetic), memory management (stack vs heap, static vs dynamic allocation), and dynamic memory allocation functions (malloc, calloc, realloc, free) with practical applications in data structures and real-world scenarios like sorting records or handlin

TAKEAWAYS:

  • Pointers store memory addresses and enable direct memory manipulation, enabling efficient data structures like linked lists and dynamic arrays.
  • Memory in C is divided into stack (automatic, fixed-size) and heap (dynamic, variable-size) regions, each with distinct allocation rules.
  • Dynamic memory allocation (malloc, calloc) allows runtime memory allocation for flexible data structures (e.g., arrays of unknown size).
  • Dangling pointers (accessing freed memory) and memory leaks (unfreed blocks) are critical bugs requiring careful pointer management.
  • Pointers to structures/unions enable efficient traversal and modification of complex data (e.g., sorting employee records).
  • Real-world uses include Khalti’s transaction logs (dynamic arrays for variable transactions) and Ncell’s call routing (pointer-based linked lists for call queues).

1. Pointers: The Foundation

head102030NULL
Linked list node with pointer to next: `struct Node { int data; struct Node *next; };`
100201302ptr (points to arr[0])ptr + 1 (points to arr[1])
Pointer arithmetic in an array: `int arr[3] = {10, 20, 30}; int *ptr = arr;`

1.1 What is a Pointer?

A pointer is a variable that stores the memory address of another variable. It allows indirect access to data, enabling efficient memory operations.

classDiagram
    class Pointer {
        +int* ptr
        +void assign(int* address)
        +int dereference() int
    }
    class Variable {
        +int value
    }
    Pointer --> Variable : points to

Key Operations:

  • Declaration: int *ptr; (declares a pointer to an integer).
  • Assignment: ptr = &var; (stores the address of var).
  • Dereferencing: *ptr = 10; (accesses/modifies the value at the address).

1.2 Pointer Arithmetic

Pointers can be incremented/decremented to traverse memory (e.g., arrays). Each increment moves by the size of the data type (e.g., int* moves by 4 bytes on most systems).

int arr[3] = {10, 20, 30};
int *ptr = arr; // ptr points to arr[0]
printf("%d", *(ptr + 1)); // Output: 20 (arr[1])

Visualization: Pointer Arithmetic in an Array


1.3 Special Pointers

Type Description Example
Null Pointer Points to nothing (address 0). Prevents undefined behavior. int *ptr = NULL;
Void Pointer Generic pointer (can point to any data type). Requires casting. void *ptr = malloc(10);
Dangling Pointer Points to freed memory. Causes crashes. free(ptr); *ptr = 5; (dangerous)
Wild Pointer Uninitialized pointer (garbage address). int *ptr; (not assigned)

2. Memory Management in C

main()func1()func2()TOP (current stack frame)
Stack frames during nested function calls (LIFO order)

2.1 Stack vs. Heap Memory

Feature Stack Heap
Allocation Automatic (compiler-managed) Manual (malloc, calloc)
Size Fixed (small, ~1-8 MB) Large (limited by system)
Lifetime Scope-bound (freed on block exit) Persists until free()
Speed Faster (no OS overhead) Slower (OS allocation)
Use Case Local variables, function calls Dynamic data (arrays, structures)

Visualization: Stack vs. Heap


2.2 Static vs. Dynamic Memory Allocation

Static Dynamic
Allocated at compile-time Allocated at runtime
Fixed size Variable size
Example: int arr[10]; Example: int *arr = malloc(10*sizeof(int));
Limitations: Advantages:
- Wastes memory if size is overestimated. - Flexible (allocates only what’s needed).
- Cannot resize. - Supports complex data structures (linked lists, trees).

3. Dynamic Memory Allocation (DMA)

flowchart TD
    A["Allocate: `ptr = malloc(5*sizeof(int))`"] --> B["Store values: `ptr[0] = 10`"]
    B --> C["Resize: `ptr = realloc(ptr, 10*sizeof(int))`"]
    C --> D["Free: `free(ptr)`"]
Lifecycle of dynamic memory allocation

3.1 Key Functions

Function Description Example
malloc() Allocates uninitialized memory. ptr = malloc(10 * sizeof(int));
calloc() Allocates zero-initialized memory. ptr = calloc(10, sizeof(int));
realloc() Resizes previously allocated memory. ptr = realloc(ptr, 20 * sizeof(int));
free() Releases allocated memory (prevents leaks). free(ptr);

Example: Allocating and Freeing Memory

int *arr = malloc(5 * sizeof(int)); // Allocates 5 integers
if (arr == NULL) { /* Handle error */ }
arr[0] = 10; // Store value
free(arr);   // Release memory

3.2 Common Pitfalls

  1. Memory Leak: Forgetting to free() allocated memory.
    int *ptr = malloc(100);
    // ... (no free(ptr)) → Leak!
    
  2. Dangling Pointer: Using a pointer after free().
    int *ptr = malloc(10);
    free(ptr);
    *ptr = 5; // Undefined behavior!
    
  3. Wild Pointer: Using an uninitialized pointer.
    int *ptr;
    *ptr = 10; // Crash (ptr may point to invalid memory)
    

Visualization: Memory Leak



4. Pointers and Structures

id: 1name: Alicesalary: 50000Employee
Structure with pointer to its fields: `struct Employee *e;`

4.1 Pointers to Structures

Structures can be passed to functions via pointers for efficiency (avoids copying large data).

struct Employee {
    int id;
    char name[50];
    float salary;
};

void printEmp(struct Employee *e) {
    printf("ID: %d, Name: %s", e->id, e->name);
}

Example: Sorting Employee Records

#include <stdio.h>
#include <stdlib.h>

struct Employee {
    int id;
    char name[50];
    float salary;
};

int compare(const void *a, const void *b) {
    return ((struct Employee *)a)->salary - ((struct Employee *)b)->salary;
}

int main() {
    struct Employee *emp = malloc(3 * sizeof(struct Employee));
    emp[0] = (struct Employee){1, "Alice", 50000};
    emp[1] = (struct Employee){2, "Bob", 30000};
    emp[2] = (struct Employee){3, "Charlie", 70000};

    qsort(emp, 3, sizeof(struct Employee), compare);

    for (int i = 0; i < 3; i++)
        printf("%s: %.2f\n", emp[i].name, emp[i].salary);
    free(emp);
    return 0;
}

Output:

Bob: 30000.00
Alice: 50000.00
Charlie: 70000.00

Visualization: Structure with Pointer

![structure with pointer diagram](/media/d5602ec859002f1a60b8.png "A struct node with a pointer to the next node (linked list). (Image: Qwertyus, CC BY-SA 4.0, via Wikimedia Commons)")

5. Real-World Applications

In the Real World

  1. Khalti’s Transaction Logs

    • Idea Used: Dynamic arrays (pointer-based) to store variable numbers of transactions.
    • How: malloc() allocates memory for each new transaction, and realloc() resizes the log as needed.
  2. Ncell’s Call Routing System

    • Idea Used: Linked lists (pointers to struct Call) to manage call queues.
    • How: Each call is a node with next pointer, enabling efficient FIFO processing.
  3. Daraz’s Order Processing

    • Idea Used: Pointers to structures (struct Order) for tracking orders.
    • How: malloc() creates an order record, and free() clears completed orders.

Worked Example: Bank Loan Interest Calculation

#include <stdio.h>

float calculateInterest(float *principal, float rate, int years) {
    return (*principal * rate * years) / 100;
}

int main() {
    float principal = 100000;
    float rate = 5.5;
    int years = 3;
    float interest = calculateInterest(&principal, rate, years);
    printf("Interest: %.2f\n", interest); // Output: 16500.00
    return 0;
}

Trace Table:

Step principal rate years interest Calculation
1 100000 5.5 3 (100000 * 5.5 * 3) / 100 = 16500

6. Exam Tip

  1. Pointer Syntax: Always declare pointers correctly (int *ptr; not int* ptr; unless intentional).
  2. DMA Functions: Remember:
    • malloc(): Uninitialized.
    • calloc(): Zero-initialized.
    • realloc(): Resizes existing memory.
    • free(): Always free dynamically allocated memory.
  3. Common Exam Questions:
    • Write a program to swap two numbers using pointers.
    • Explain dangling pointers and how to avoid them.
    • Use malloc() to store N integers and find the sum.
  4. Avoid Memory Leaks: In programs using malloc, ensure every allocation has a corresponding free().
  5. Pointer Arithmetic: Understand how ptr + 1 behaves differently for int* vs char*.

Key Formula (Interest Calculation):

In the real world

  • Khalti’s Transaction Logs: Uses dynamic arrays (resizable via realloc) to store variable-length transaction records (e.g., struct Transaction *logs = malloc(N*sizeof(struct Transaction))), where N grows with new payments. Each transaction pointer (logs[i]) holds merchant ID, amount, and timestamp.
  • Ncell’s Call Routing: Implements linked lists (via struct CallNode *next) to manage call queues dynamically. When a call arrives, a new node is malloc’d and appended to the queue’s rear, while the front node is free’d when the call ends.
  • Nepali Banks’ Loan Processing: Employs pointers to structures (e.g., struct Loan *loans = malloc(1000*sizeof(struct Loan))) to track borrower details (ID, amount, interest rate) and sort them by priority using qsort with a custom comparator.

Based on the TU BCA syllabus for C Programming (CACS151), unit 8.

Discussion

Loading…