Quiz 2

Pointers Deep Dive — Pointer Arithmetic, Arrays vs Pointers, Function Pointers

1505 words
8 min read
Python Week 1: the first filter for runtime behavior
Visual companion
Python
Type and operator map

Python Week 1: the first filter for runtime behavior

View
Revision summary

What this note is really saying

Short form

# Pointers Deep Dive — Pointer Arithmetic, Arrays vs Pointers, Function Pointers ## 🎯 Learning Objectives - Declare and use pointers to various types - Perform pointer arithmetic correctly - Differentiate arrays and pointers - Use pointers with functions (call by reference) - Implement dynamic data structures using...

Pointers Deep Dive — Pointer Arithmetic, Arrays vs Pointers, Function Pointers

🎯 Learning Objectives

  • Declare and use pointers to various types
  • Perform pointer arithmetic correctly
  • Differentiate arrays and pointers
  • Use pointers with functions (call by reference)
  • Implement dynamic data structures using pointers

1. Pointer Fundamentals

1.1 Intuition

A pointer is a variable that stores the memory address of another variable. Instead of holding a value directly (like int x = 5), a pointer holds the address where the value lives. Think of it like a house address — you can find the house (value) by going to the address.

1.2 Declaration and Initialization

c
int x = 42;       // x is an integer variable at some memory address
int *p;           // p is a pointer to an integer
p = &x;           // p now holds the address of x
printf("Value of x: %d\n", x);    // 42
printf("Address of x: %p\n", &x); // e.g., 0x7ffd5a1e4b1c
printf("Value of p: %p\n", p);    // same address as &x
printf("Value at *p: %d\n", *p);  // 42 (dereferencing)

1.3 Memory Layout

(Diagram)

1.4 Pointer Types and Sizes

DeclarationMeaningSize (64-bit)
char *pPointer to char8 bytes
int *pPointer to int8 bytes
float *pPointer to float8 bytes
double *pPointer to double8 bytes
void *pGeneric pointer8 bytes
All pointers are the same size (8 bytes on 64-bit systems) — they just differ in what they point to, which determines pointer arithmetic behavior.

2. Pointer Arithmetic

2.1 Rules

c
int arr[5] = {10, 20, 30, 40, 50};
int *p = arr;  // Points to arr[0]
p + 1;   // Points to arr[1] (address increases by sizeof(int) = 4 bytes)
p + 2;   // Points to arr[2] (address increases by 8 bytes)
p - 1;   // Points to arr[-1] (one position before start — dangerous!)
Addition/subtraction is scaled by the size of the pointed-to type:
c
printf("arr = %p\n", arr);      // e.g., 0x1000
printf("arr+1 = %p\n", arr+1);  // 0x1004 (not 0x1001!)
printf("arr+2 = %p\n", arr+2);  // 0x1008

2.2 Difference of Pointers

c
int *p1 = &arr[0];
int *p2 = &arr[3];
ptrdiff_t diff = p2 - p1;  // 3 (3 elements apart, NOT 12 bytes!)
The result is the number of elements between the two pointers, not bytes.

2.3 Pointer Comparison

c
if (p1 < p2) {
    printf("p1 points to an earlier element\n");
}
Comparisons are valid only for pointers to the same array.

3. Arrays and Pointers

3.1 Array Name as Pointer

c
int arr[5] = {10, 20, 30, 40, 50};
// arr is a constant pointer to arr[0]
// arr + i is equivalent to &arr[i]
// *(arr + i) is equivalent to arr[i]
// Array subscript is pointer arithmetic:
// arr[i] == *(arr + i) == *(i + arr) == i[arr]  (yes, i[arr] works!)
printf("3[arr] = %d\n", 3[arr]);  // 40

3.2 Key Difference: sizeof

c
int arr[5] = {10, 20, 30, 40, 50};
int *p = arr;
printf("sizeof(arr) = %zu\n", sizeof(arr));  // 20 (5 × 4 bytes)
printf("sizeof(p) = %zu\n", sizeof(p));      // 8 (pointer size)
// Getting array size from pointer won't work!
int size = sizeof(p) / sizeof(p[0]);  // 8/4 = 2 (WRONG!)

3.3 Arrays Decay to Pointers

c
void print_array(int *arr, int size) {  // arr decays to pointer
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);  // Works because arr[i] == *(arr + i)
    }
}
int main() {
    int data[5] = {1, 2, 3, 4, 5};
    print_array(data, 5);  // data decays to &data[0]
    // sizeof(data) in main() is 20, but sizeof(arr) in function is 8!
}

4. Pointers and Functions

4.1 Call by Reference

c
void swap(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}
int main() {
    int x = 5, y = 10;
    swap(&x, &y);
    printf("x=%d, y=%d\n", x, y);  // x=10, y=5
    return 0;
}

4.2 Pointer to Pointer (Double Pointer)

c
int x = 42;
int *p = &x;    // p points to x
int **pp = &p;  // pp points to p
printf("x = %d\n", x);        // 42
printf("*p = %d\n", *p);      // 42
printf("**pp = %d\n", **pp);  // 42
(Diagram)

4.3 Function Pointers

c
#include <stdio.h>
int add(int a, int b) { return a + b; }
int subtract(int a, int b) { return a - b; }
int main() {
    // Declare a function pointer
    int (*operation)(int, int);
    operation = add;
    printf("add(5, 3) = %d\n", operation(5, 3));  // 8
    operation = subtract;
    printf("subtract(5, 3) = %d\n", operation(5, 3));  // 2
    return 0;
}

5. Dynamic Memory Allocation

5.1 malloc, calloc, realloc, free

c
// malloc: allocate uninitialized memory
int *arr = (int*)malloc(5 * sizeof(int));
// calloc: allocate zero-initialized memory
int *arr2 = (int*)calloc(5, sizeof(int));
// realloc: resize existing allocation
arr = (int*)realloc(arr, 10 * sizeof(int));
// free: release memory
free(arr);
free(arr2);

5.2 Common Dynamic Memory Patterns

c
// Create a dynamic array
int *create_array(int size) {
    int *arr = (int*)malloc(size * sizeof(int));
    if (arr == NULL) {
        fprintf(stderr, "Memory allocation failed\n");
        exit(1);
    }
    return arr;
}
// 2D array using pointer-to-pointer
int **matrix = (int**)malloc(rows * sizeof(int*));
for (int i = 0; i < rows; i++) {
    matrix[i] = (int*)malloc(cols * sizeof(int));
}

6. Common Pitfalls

Pitfall 1: Dereferencing NULL or uninitialized pointers

c
int *p;
*p = 42;  // SEGFAULT! p is uninitialized
int *q = NULL;
*q = 42;  // SEGFAULT! dereferencing NULL
Always initialize pointers. Set to NULL if not immediately assigned.

Pitfall 2: Buffer overflow

c
int arr[5];
for (int i = 0; i <= 5; i++) {  // Off-by-one!
    arr[i] = i * 10;  // arr[5] writes beyond allocated memory
}

Pitfall 3: Memory leaks

c
void leak() {
    int *p = (int*)malloc(100 * sizeof(int));
    // Never call free(p)!
    return;
}
Always free dynamically allocated memory. Use tools like valgrind to detect leaks.

Pitfall 4: Dangling pointers

c
int *p = (int*)malloc(sizeof(int));
free(p);
*p = 42;  // Dangling pointer! p still points to freed memory
Set freed pointers to NULL to catch use-after-free bugs.

7. 📐 Key Formulas / Concepts

ConceptExpressionMeaning
Address-of&xMemory address of variable x
Dereference*pValue at address p
Array subscriptarr[i]*(arr + i) — pointer arithmetic
Pointer differencep2 - p1Number of elements between pointers
Arrow operatorp->member(*p).member — access struct member via pointer

8. 📝 Practice Questions

Q1: What is the output? int arr[] = {10, 20, 30, 40}; int *p = arr; printf("%d", *(p+2) + 3);
Answer: 33. *(p+2) = arr[2] = 30. 30 + 3 = 33. Q2: Explain why sizeof(arr) in a function parameter gives the wrong result.
Answer: When an array is passed to a function, it "decays" to a pointer to its first element. In the function parameter, int arr[] is equivalent to int *arr, and sizeof(arr) returns the size of a pointer (8 bytes on 64-bit), not the array size. Q3: Write a function that takes a string and reverses it in-place using pointers.
c
void reverse(char *str) {
    char *start = str;
    char *end = str;
    while (*end) end++;  // Find end of string
    end--;               // Point to last character

    while (start < end) {
        char temp = *start;
        *start = *end;
        *end = temp;
        start++;
        end--;
    }
}
Q4: What is a memory leak and how can you prevent it?
Answer: A memory leak occurs when dynamically allocated memory is not freed after use. Prevent by: (1) always pairing malloc with free, (2) using tools like valgrind, (3) following RAII-like patterns (allocate in constructor, free in destructor), (4) setting freed pointers to NULL. Q5: What is the difference between char *s = "hello" and char s[] = "hello"?
Answer: char *s = "hello" creates a pointer to a string literal (read-only, stored in .rodata). char s[] = "hello" creates a mutable array (stored on stack, can be modified). Modifying a string literal via *s is undefined behavior (usually crashes). Q6: Write a function that returns a pointer to the maximum element in an array.
c
int *max_element(int *arr, int size) {
    if (size == 0) return NULL;
    int *max = arr;
    for (int i = 1; i < size; i++) {
        if (arr[i] > *max) max = &arr[i];
    }
    return max;
}

9. 🔗 Cross-References

  • Week 5 - Dynamic Memory: malloc, calloc, free deep dive
  • Week 6 - Structures: Pointers to structs, linked lists
  • BSCS4022 (OS): System calls, process memory layout
  • BSCS3031 (CSD): Address translation, memory hierarchy Join Discord PreviousControl Flow & ArraysNextFunctions
Document outline

Keep your place and jump directly to a heading.

Table of Contents
System Normal // Awaiting Context

Intelligence Hub

Navigate the knowledge graph to generate context. The Hub adapts dynamically to surface backlinks, related notes, and metadata insights.