Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Pointers and Arrays

In the previous lesson, you learned what a POINTER is: a variable that stores a memory address. Now, we will explore one of the most important relationships in C: the connection between pointers and arrays.

THE BIG REVEAL: The name of an array, by itself, acts as a CONSTANT POINTER to the first element of that array.

This means an array’s name IS a memory address. This is why we didn’t need the & operator when using scanf with a string (which is a char array) back in Lesson 3. The array’s name already provided the address!

This relationship enables a powerful technique called POINTER ARITHMETIC, which allows us to move between elements of an array using a pointer.

Full Source

/**
 * @file 11_pointers_and_arrays.c
 * @brief Part 2, Lesson 11: Pointers and Arrays
 * @author dunamismax
 * @date 06-15-2025
 *
 * This file serves as the lesson and demonstration for the relationship
 * between pointers and arrays. It explains that an array name is a pointer
 * to the first element and introduces the concept of POINTER ARITHMETIC.
 */

/*
 * =====================================================================================
 * |                                   - LESSON START -                                  |
 * =====================================================================================
 *
 * In the previous lesson, you learned what a POINTER is: a variable that stores
 * a memory address. Now, we will explore one of the most important relationships
 * in C: the connection between pointers and arrays.
 *
 * THE BIG REVEAL:
 * The name of an array, by itself, acts as a CONSTANT POINTER to the first
 * element of that array.
 *
 * This means an array's name IS a memory address. This is why we didn't need the
 * `&` operator when using `scanf` with a string (which is a `char` array) back
 * in Lesson 3. The array's name already provided the address!
 *
 * This relationship enables a powerful technique called POINTER ARITHMETIC, which
 * allows us to move between elements of an array using a pointer.
 */

#include <stdio.h>

int main(void)
{
    // Let's create an array of integers.
    // In memory, these values are stored right next to each other in a
    // single, unbroken block. This is called CONTIGUOUS memory.
    int grades[] = {85, 92, 78, 99, 67};
    int count = 5; // Number of elements in the array

    // --- Part 1: The Array Name is a Pointer ---

    printf("--- Part 1: The Array Name as a Pointer ---\n");

    // Let's prove that the array name 'grades' is just an address.
    // We will print the value of 'grades' and the address of its first element.
    printf("Value of 'grades' (the array name): %p\n", (void *)grades);
    printf("Address of the first element (&grades[0]): %p\n", (void *)&grades[0]);
    printf("They are the same!\n\n");

    // --- Part 2: Pointer Arithmetic ---

    printf("--- Part 2: Pointer Arithmetic ---\n");

    // Since the array name is a pointer, we can assign it to a real pointer variable.
    int *p_grades = grades; // Note: no `&` is needed!

    // Let's access the first element using the pointer.
    printf("First element using grades[0]: %d\n", grades[0]);
    printf("First element using *p_grades: %d\n\n", *p_grades);

    // Now for the magic. What happens if we add 1 to the pointer?
    // POINTER ARITHMETIC is smart. Adding `1` to an `int` pointer doesn't just
    // add 1 to the address. It moves the address forward by the size of ONE ELEMENT.
    // In this case, it moves forward by `sizeof(int)` bytes.

    // `*(p_grades + 1)` means "go to the address of the first element, move
    // forward by the size of one integer, and then dereference that new address."
    printf("Second element using grades[1]: %d\n", grades[1]);
    printf("Second element using *(p_grades + 1): %d\n\n", *(p_grades + 1));

    printf("Third element using grades[2]: %d\n", grades[2]);
    printf("Third element using *(p_grades + 2): %d\n\n", *(p_grades + 2));

    // --- Part 3: Equivalence of p[i] and *(p + i) ---

    printf("--- Part 3: Equivalence of Subscript and Pointer Notation ---\n");

    // The C language guarantees that for any pointer or array `p` and integer `i`,
    // the expression `p[i]` is EXACTLY THE SAME as `*(p + i)`.
    // The square bracket notation `[]` is just "syntactic sugar" to make pointer
    // arithmetic look cleaner and more familiar.

    printf("Accessing the fourth element (index 3):\n");
    printf("Using array notation grades[3]: %d\n", grades[3]);
    printf("Using pointer notation *(grades + 3): %d\n\n", *(grades + 3));

    // This even works on our pointer variable `p_grades`!
    printf("Accessing the fifth element (index 4) via our pointer variable:\n");
    printf("Using pointer notation *(p_grades + 4): %d\n", *(p_grades + 4));
    printf("Using array notation p_grades[4]: %d\n\n", p_grades[4]);

    // --- Part 4: Looping Through an Array with a Pointer ---

    printf("--- Part 4: Looping Through The Array ---\n");
    printf("Grades: ");
    for (int i = 0; i < count; i++)
    {
        // We will use pointer arithmetic to access each element
        printf("%d ", *(grades + i));
    }
    printf("\n");

    return 0;
}

/*
 * =====================================================================================
 * |                                    - LESSON END -                                   |
 * =====================================================================================
 *
 * Key Takeaways:
 *
 * 1.  The name of an array is a constant pointer to its first element.
 * 2.  `array_name` and `&array_name[0]` evaluate to the same memory address.
 * 3.  POINTER ARITHMETIC lets you move between elements. Adding `n` to a pointer
 *     moves it forward by `n * sizeof(element_type)` bytes in memory.
 * 4.  The standard array subscript notation `array[i]` is just a more convenient
 *     way of writing the pointer arithmetic expression `*(array + i)`. They are
 *     functionally identical.
 * 5.  This deep relationship is what makes passing arrays to functions so efficient
 *     in C--you're just passing a single address, not copying the whole array.
 *
 * HOW TO COMPILE AND RUN THIS CODE:
 *
 * 1. Open a terminal or command prompt.
 * 2. Navigate to the directory where you saved this file.
 * 3. Use the GCC compiler to create an executable file:
 *    `gcc -Wall -Wextra -std=c11 -o 11_pointers_and_arrays 11_pointers_and_arrays.c`
 * 4. Run the executable:
 *    - On Linux/macOS:   `./11_pointers_and_arrays`
 *    - On Windows:       `11_pointers_and_arrays.exe`
 */

How to Compile and Run

cc -Wall -Wextra -std=c11 -o 11_pointers_and_arrays 11_pointers_and_arrays.c
./11_pointers_and_arrays