[C Series 9] Understanding Arrays and Pointers

한국어 버전

With a solid pointer baseline from part 8, we can now connect arrays and pointers. In most expressions, an array name acts like the address of its first element. That is why you need pointers to understand arrays and arrays to see pointers in action.

New Terms in This Chapter

  1. Array Decay: The conversion of an array name into a pointer to its first element within an expression
  2. Pointer Arithmetic: Adding or subtracting integers from a pointer to move to neighboring elements
  3. Array Parameter: A function parameter that looks like an array but actually receives a pointer
  4. Length Information: The explicit number of elements you must pass along to process an array safely

Key Ideas

Study Notes

  • Time: 60–70 minutes
  • Prep: experience looping over arrays and declaring/dereferencing pointers
  • Goal: describe how array names relate to pointers and pass arrays to functions safely

Keep these four ideas in mind:

  • Array names act like the address of the first element in many expressions.
  • arr[i] and *(arr + i) access the same element.
  • Pointer arithmetic moves in units of the underlying data type.
  • Always pass length information along with an array when calling functions.

Follow Along with Code

Comparing an Array Name to the First Element's Address

#include <stdio.h>

int main(void) {
    int numbers[3] = {10, 20, 30};

    printf("numbers = %p\n", (void *)numbers);
    printf("&numbers[0] = %p\n", (void *)&numbers[0]);
    printf("sizeof(numbers) = %zu\n", sizeof(numbers));
    printf("sizeof(&numbers[0]) = %zu\n", sizeof(&numbers[0]));
    return 0;
}

The printed addresses usually match, but note the difference in sizes. sizeof(numbers) is the size of the entire array, while sizeof(&numbers[0]) is just the size of a pointer. In other words, an array name is not itself a pointer, even though it decays to a pointer in many expressions.

arr[i] vs. *(arr + i)

#include <stdio.h>

int main(void) {
    int numbers[4] = {3, 6, 9, 12};
    int i;

    for (i = 0; i < 4; i++) {
        printf("numbers[%d] = %d\n", i, numbers[i]);
        printf("*(numbers + %d) = %d\n", i, *(numbers + i));
    }

    return 0;
}

numbers + i points to the address of element i, and *(numbers + i) reads the value stored there. That is why the subscript operator and pointer arithmetic are two views of the same access pattern.

Pointer Arithmetic Respects the Element Size

#include <stdio.h>

int main(void) {
    int values[3] = {100, 200, 300};
    int *ptr = values;

    printf("ptr = %p\n", (void *)ptr);
    printf("ptr + 1 = %p\n", (void *)(ptr + 1));
    printf("*(ptr + 1) = %d\n", *(ptr + 1));
    return 0;
}

ptr + 1 does not simply add 1 to the numeric address—it skips ahead by the size of one int. Pointer arithmetic only makes sense while you stay within the same array.

Passing an Array to a Function

When you pass an array as an argument, the function effectively receives a pointer.

#include <stdio.h>

void print_scores(int scores[], int length) {
    int i;

    for (i = 0; i < length; i++) {
        printf("scores[%d] = %d\n", i, scores[i]);
    }
}

int main(void) {
    int scores[4] = {95, 82, 74, 63};

    print_scores(scores, 4);
    return 0;
}

Although the parameter is written as scores[], the function only knows the address of the first element. That is why it needs the length as a second argument. This is one of the most important C habits: when you pass an array to a function, pass its length too.

Read-Only Array Parameters

Use const when the function will not modify the array.

#include <stdio.h>

void print_total(const int numbers[], int length) {
    int i;
    int total = 0;

    for (i = 0; i < length; i++) {
        total += numbers[i];
    }

    printf("total = %d\n", total);
}

int main(void) {
    int numbers[3] = {5, 10, 15};

    print_total(numbers, 3);
    return 0;
}

const documents the fact that the function will only read the data. It does not magically supply the length, so you still have to pass that separately.

Practical Example: Finding the Highest Score

#include <stdio.h>

int find_max(const int values[], int length) {
    int i;
    int max = values[0];

    for (i = 1; i < length; i++) {
        if (values[i] > max) {
            max = values[i];
        }
    }

    return max;
}

int main(void) {
    int scores[5] = {72, 88, 91, 67, 85};

    printf("max = %d\n", find_max(scores, 5));
    return 0;
}

This example passes an array into a function, combines loops and conditionals, and returns a meaningful result. It shows how the topics from earlier chapters line up in one short program.

Why It Matters

You need to understand the array–pointer relationship to reason about function arguments, string handling, and dynamic memory. Without that connection, later C topics feel disconnected.

Remember these four points:

  • Array names usually behave like the address of the first element.
  • Brackets and pointer arithmetic are two equivalent ways to reach an element.
  • Always send length information along with an array.
  • Use const when a function should only read from the array.

Practice in CodeSandbox

The sandbox below uses CodeSandbox's Universal starter. For C, the key learning loop is still compile and run in the terminal, so recreate the lesson code as a source file and repeat that cycle directly.

Live Practice

C Practice Sandbox

CodeSandbox

Run the starter project in CodeSandbox, compare it with the lesson code, and keep experimenting.

Universal starterCterminal
  1. Fork the starter and create a practice file such as hello.c
  2. Paste in the lesson code and compile it if clang or gcc is available
  3. Edit the code, rebuild it, and compare the new output

For C, the terminal build loop matters more than a browser preview. Compiler availability can vary by environment, so first confirm that clang or gcc is present in the Universal starter.

Practice

  • Follow: Run the example and compare the printed values of numbers and &numbers[0].
  • Extend: Modify find_max into a find_min function that returns the lowest score.
  • Debug: Purposely pass the wrong length to a function and explain why the results go wrong.
  • Done When: You can explain array names, pointer arithmetic, and length passing, and you can write your own functions that take arrays.

Wrap-Up

We saw why arrays and pointers always appear together. The goal is not to pretend they are identical but to understand that "an array name often behaves like the address of the first element." In the next chapter we will group multiple related values into richer data structures using struct, enum, and typedef.

💬 댓글

이 글에 대한 의견을 남겨주세요