Programming In CUnit 89 min read
Pointers, Dynamic Memory & Memory Management in C
Unit 8 of Programming In C covers pointers (addresses, pointer arithmetic, pointer-to-pointer), dynamic memory allocation (malloc, calloc, realloc, free), memory leaks, dangling pointers, and practical applications like linked lists, flexible data structures, and efficient array handling.
TAKEAWAYS:
- Pointers store memory addresses and enable direct memory access, enabling efficient array traversal, dynamic memory management, and function parameter passing.
- Dynamic memory allocation (
malloc,calloc,realloc,free) allows runtime memory allocation, crucial for scalable programs like databases or large-scale simulations. - Memory leaks and dangling pointers are common pitfalls; always pair
malloc/callocwithfreeand validate pointers before dereferencing. - Pointer arithmetic (
ptr + n,ptr++) works on byte offsets, not logical increments, making it essential for low-level memory manipulation. - Dynamic memory management is used in real-world applications like Khalti’s transaction logs (flexible memory for variable-sized records) and Pathao’s ride-matching system (dynamic allocation for real-time driver-passenger pairing).
1. Pointers: The Foundation of Memory Access
1.1 What is a Pointer?
A pointer is a variable that stores the memory address of another variable. It enables indirect access to data, allowing efficient memory manipulation.
int x = 10; // Integer variable
int *ptr = &x; // Pointer storing address of x
&x→ Address-of operator (returns memory address ofx).*ptr→ Dereference operator (accesses value at address stored inptr).
1.2 Why Use Pointers?
| Feature | Without Pointers | With Pointers |
|---|---|---|
| Memory Efficiency | Copies data (inefficient for large data) | Works with addresses (direct access) |
| Dynamic Allocation | Fixed-size arrays only | Allocate/deallocate memory at runtime |
| Function Parameters | Pass-by-value (copies) | Pass-by-reference (modifies original) |
| Data Structures | Limited to static arrays | Enables linked lists, trees, graphs |
1.3 Pointer Arithmetic
Pointers can be incremented/decremented to traverse memory locations. The operation depends on the data type of the pointer:
ptr + 1→ Moves to the next memory location of the same type (e.g.,int*moves by 4 bytes,char*by 1 byte).ptr++→ Increments the pointer to the next element.
int arr[3] = {10, 20, 30};
int *ptr = arr; // Points to first element
printf("%d", *(ptr + 1)); // Output: 20 (skips 1 int-sized step)
ptr++; // Now points to 20
Visual: Pointer Arithmetic in an Array
1.4 Pointer-to-Pointer (Double Pointer)
A pointer that stores the address of another pointer. Used for:
- Modifying pointers inside functions.
- Creating 2D arrays dynamically.
int x = 100;
int *ptr = &x;
int **pptr = &ptr; // pptr stores address of ptr
printf("%d", **pptr); // Output: 100 (dereferences twice)
Real-World Example: Khalti’s Transaction Logs Khalti uses pointer-to-pointer to dynamically manage transaction records. Each transaction is stored in a linked list where:
- The head pointer (
ptr) points to the first transaction. - A double pointer (
**pptr) allows the system to update the head when new transactions arrive (e.g., during peak hours like Dashain).
2. Dynamic Memory Allocation
Static memory (e.g., arrays) has fixed size. Dynamic memory allows allocation/deallocation at runtime using:
malloc()→ Allocates uninitialized memory.calloc()→ Allocates and initializes to zero.realloc()→ Resizes previously allocated memory.free()→ Releases allocated memory.
2.1 malloc() vs calloc()
| Function | Syntax | Initialization | Use Case |
|---|---|---|---|
malloc |
ptr = (type*)malloc(size) |
Uninitialized | General-purpose allocation |
calloc |
ptr = (type*)calloc(n, size) |
Zero-initialized | Arrays, structures needing defaults |
int *arr1 = (int*)malloc(5 * sizeof(int)); // Uninitialized
int *arr2 = (int*)calloc(5, sizeof(int)); // All zeros
2.2 Memory Leaks and Dangling Pointers
- Memory Leak: Forgetting to
free()allocated memory.int *ptr = malloc(sizeof(int)); ptr = NULL; // Leak! Original memory lost. - Dangling Pointer: Pointer referencing freed memory.
int *ptr = malloc(sizeof(int)); free(ptr); printf("%d", *ptr); // Undefined behavior!
Visual: Memory Leak in a Program
flowchart TD
A["Allocate 100 bytes"] --> B["Use memory"]
B --> C["Forget to free()"]
C --> D["Memory Leak: 100 bytes lost"]2.3 Real-World Example: Daraz’s Order Queue
Daraz uses dynamic memory allocation to handle variable-sized order queues during sales (e.g., Dashain discounts). Instead of preallocating a fixed-size array, it:
- Allocates memory for orders dynamically using
malloc. - Uses
reallocto resize the queue if orders exceed capacity. - Frees memory after order processing to avoid leaks.
Code Example: Dynamic Array for Orders
#include <stdio.h>
#include <stdlib.h>
int main() {
int *orders = NULL;
int size = 0, capacity = 2;
// Simulate adding orders dynamically
orders = (int*)malloc(capacity * sizeof(int));
for (int i = 0; i < 5; i++) {
if (i >= capacity) {
capacity *= 2;
orders = (int*)realloc(orders, capacity * sizeof(int));
}
orders[i] = i + 1; // Order ID
}
// Display orders
for (int i = 0; i < 5; i++) {
printf("Order %d: %d\n", i + 1, orders[i]);
}
free(orders); // Prevent memory leak
return 0;
}
Output:
Order 1: 1
Order 2: 2
Order 3: 3
Order 4: 4
Order 5: 5
3. Pointers and Arrays: The Connection
Arrays decay into pointers when passed to functions. This is why:
arrand&arr[0]are equivalent.sizeof(arr)in a function is not the array size (it becomes a pointer).
void printArray(int *arr, int size) {
for (int i = 0; i < size; i++) {
printf("%d ", *(arr + i)); // Equivalent to arr[i]
}
}
Visual: Array and Pointer Relationship
4. Pointers in Functions: Pass-by-Reference
Functions can modify original variables using pointers.
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
Real-World Example: Ncell’s Billing System Ncell’s billing system uses pointers to modify customer data directly without copying entire records. For example:
- A function
updateBalance(int *balance, int amount)adjusts a customer’s balance in-place. - Avoids inefficient copying of large customer databases.
5. Common Pitfalls and Best Practices
| Pitfall | Solution |
|---|---|
| Uninitialized Pointer | Always initialize pointers (NULL). |
| Dangling Pointer | Set pointers to NULL after free(). |
| Memory Leak | Use free() for every malloc/calloc. |
| Pointer Arithmetic Errors | Cast pointers explicitly when needed. |
| Double Free | Track allocations with a linked list. |
Best Practice: Always Validate Pointers
if (ptr != NULL) {
printf("%d", *ptr);
} else {
printf("Error: Null pointer!");
}
6. Dynamic Memory for Data Structures
6.1 Linked Lists
Nodes are dynamically allocated and linked using pointers.
typedef struct Node {
int data;
struct Node *next;
} Node;
Node *head = NULL;
Node *newNode = (Node*)malloc(sizeof(Node));
newNode->data = 10;
newNode->next = head;
head = newNode;
Visual: Linked List After Insertion
6.2 2D Arrays Dynamically
int **matrix = (int**)malloc(rows * sizeof(int*));
for (int i = 0; i < rows; i++) {
matrix[i] = (int*)malloc(cols * sizeof(int));
}
Real-World Example: NTC’s Traffic Route Optimization NTC uses dynamic 2D arrays to represent Kathmandu’s traffic grid. Each cell stores:
- Traffic density (updated in real-time).
- Pointers to neighboring intersections for pathfinding.
7. Exam Tip: How to Score Full Marks
- Always explain the purpose of pointers/dynamic memory (e.g., "Pointers enable efficient memory access without copying data").
- Show step-by-step traces for pointer arithmetic or dynamic allocation (e.g., "After
ptr++, the pointer moves to the nextintlocation"). - Compare
mallocvscallocin tables (as shown above). - Link to real-world systems (e.g., "Khalti uses dynamic memory for transaction logs").
- Avoid common mistakes:
- Forgetting to
free()memory. - Dereferencing
NULLpointers. - Misusing pointer arithmetic (e.g.,
char*vsint*increments).
- Forgetting to
Sample Exam Question Solution:
Q: Write a program to input n numbers in an array using dynamic memory and find the average.
A:
#include <stdio.h>
#include <stdlib.h>
int main() {
int n, *arr, sum = 0;
float avg;
printf("Enter n: ");
scanf("%d", &n);
arr = (int*)malloc(n * sizeof(int)); // Dynamic allocation
printf("Enter %d numbers:\n", n);
for (int i = 0; i < n; i++) {
scanf("%d", &arr[i]);
sum += arr[i];
}
avg = (float)sum / n;
printf("Average: %.2f\n", avg);
free(arr); // Prevent memory leak
return 0;
}
Trace for n = 3, Input: 10 20 30
| Step | arr (Memory) |
sum |
avg |
|---|---|---|---|
| Allocation | [10, 20, 30] |
0 | - |
| Loop 1 | [10, 20, 30] |
10 | - |
| Loop 2 | [10, 20, 30] |
30 | - |
| Loop 3 | [10, 20, 30] |
60 | 20.00 |
| Free | Memory released | 60 | 20.00 |
How dynamic memory works in C programs (Image: Siddharthist, CC BY-SA 4.0, via Wikimedia Commons)
Visual representation of nodes and pointers (Image: Michel Bakni, CC BY-SA 4.0, via Wikimedia Commons)
Based on the PU BE Computer (PU) syllabus for Programming In C, unit 8.
Discussion
Loading…