Back to Curriculum

Pointers and Memory Management

📚 Lesson 6 of 15 ⏱️ 50 min

Pointers and Memory Management

50 min

Pointers are variables that store memory addresses of other variables. Pointers enable indirect access to data, dynamic memory allocation, efficient function parameters, and advanced data structures. Understanding pointers is essential for C programming, as they provide low-level memory control and enable efficient programming patterns. Pointers are one of C's most powerful and challenging features.

Pointer declaration uses the asterisk (*) to indicate a pointer type. The address-of operator (&) gets a variable's address, and the dereference operator (*) accesses the value at a pointer's address. Pointers must be initialized before use, either with an address or NULL. Understanding pointer syntax enables proper pointer usage.

Pointer arithmetic allows navigation through memory by adding or subtracting integers from pointers. When you add 1 to a pointer, it advances by the size of the pointed-to type. Pointer arithmetic is commonly used with arrays, as arrays and pointers are closely related in C. Understanding pointer arithmetic enables efficient array traversal and manipulation.

Dynamic memory allocation enables programs to request memory at runtime using malloc(), calloc(), and realloc(). malloc() allocates uninitialized memory, calloc() allocates zero-initialized memory, and realloc() resizes existing allocations. All dynamically allocated memory must be freed using free() to prevent memory leaks. Understanding dynamic allocation enables flexible memory management.

Memory management is critical in C, as the language doesn't provide automatic garbage collection. Memory leaks occur when allocated memory isn't freed, causing programs to consume increasing amounts of memory. Dangling pointers point to freed memory, causing undefined behavior. Double-free errors occur when memory is freed twice. Understanding memory management prevents these common errors.

Pointers enable pass-by-reference, allowing functions to modify caller's variables. Pointers to pointers enable multi-level indirection. Function pointers enable treating functions as data. Understanding these advanced pointer uses enables sophisticated C programming patterns.

Key Concepts

  • Pointers store memory addresses of variables.
  • The & operator gets addresses, the * operator dereferences pointers.
  • Pointer arithmetic enables navigation through memory.
  • Dynamic memory allocation uses malloc()/calloc() and must be freed with free().
  • Proper memory management prevents leaks and undefined behavior.

Learning Objectives

Master

  • Understanding pointer syntax and operations
  • Using pointer arithmetic with arrays
  • Allocating and freeing dynamic memory
  • Avoiding common pointer and memory errors

Develop

  • Understanding memory management in C
  • Designing efficient pointer-based algorithms
  • Implementing safe memory management practices

Tips

  • Initialize pointers: int *ptr = NULL; or int *ptr = &var;
  • Check malloc() return: if (ptr == NULL) { /* handle error */ }.
  • Free allocated memory: free(ptr); after use to prevent leaks.
  • Use pointer arithmetic: ptr + i advances by i * sizeof(*ptr) bytes.

Common Pitfalls

  • Using uninitialized pointers, causing undefined behavior.
  • Not freeing allocated memory, causing memory leaks.
  • Dereferencing NULL pointers, causing crashes.
  • Using freed memory, causing undefined behavior.

Summary

  • Pointers enable indirect memory access and dynamic allocation.
  • Pointer arithmetic enables efficient array manipulation.
  • Dynamic memory must be allocated and freed properly.
  • Understanding pointers is essential for C programming.

Exercise

Demonstrate pointer operations and dynamic memory allocation.

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

int main() {
    // Basic pointer operations
    int num = 42;
    int *ptr = &num;
    
    printf("Value of num: %d
", num);
    printf("Address of num: %p
", (void*)&num);
    printf("Value of ptr: %p
", (void*)ptr);
    printf("Value pointed by ptr: %d
", *ptr);
    
    // Pointer arithmetic
    int arr[] = {1, 2, 3, 4, 5};
    int *arrPtr = arr;
    
    printf("Array elements using pointers:
");
    for (int i = 0; i < 5; i++) {
        printf("%d ", *(arrPtr + i));
    }
    printf("
");
    
    // Dynamic memory allocation
    int *dynamicArray = (int*)malloc(5 * sizeof(int));
    
    if (dynamicArray == NULL) {
        printf("Memory allocation failed!
");
        return 1;
    }
    
    // Initialize dynamic array
    for (int i = 0; i < 5; i++) {
        dynamicArray[i] = (i + 1) * 10;
    }
    
    printf("Dynamic array elements:
");
    for (int i = 0; i < 5; i++) {
        printf("%d ", dynamicArray[i]);
    }
    printf("
");
    
    // Free allocated memory
    free(dynamicArray);
    
    // Pointer to pointer
    int value = 100;
    int *ptr1 = &value;
    int **ptr2 = &ptr1;
    
    printf("Value: %d
", value);
    printf("Value via ptr1: %d
", *ptr1);
    printf("Value via ptr2: %d
", **ptr2);
    
    return 0;
}

Exercise Tips

  • Always check malloc() return: if (ptr == NULL) handle allocation failure.
  • Free memory after use: free(ptr); to prevent memory leaks.
  • Set pointer to NULL after free: ptr = NULL; to prevent dangling pointers.
  • Use calloc() for zero-initialized memory: calloc(count, size).

Code Editor

Output