Pointers and arrays

Intermediate

Introduction

Arrays and pointers are closely tied in C. Used as a value, an array's name turns into the address of its first element.

Once you see that an array name is really an address, a great deal of C makes sense: why arrays are not copied into functions, why strings need no &, and why sizeof can give a surprising answer.

How it works

Walking with a pointer

#include <stdio.h>

int main(void) {
    int nums[4] = {10, 20, 30, 40};
    int *p = nums;
    printf("%d %d\n", *p, *(p + 2));
    p++;
    p++;
    printf("%d %d\n", *p, p[1]);
    int sum = 0;
    for (int *q = nums; q < nums + 4; q++) sum += *q;
    printf("sum %d\n", sum);
    return 0;
}
Open in compiler

int *p = nums; points p at nums[0]. Watch the arrow: p++ does not add 1 byte, it moves to the next int (4 bytes further), so the arrow steps along the cells. *(p + 2) and p[2] mean exactly the same thing.

Syntax

int a[4] = {1, 2, 3, 4};
int *p = a;
*(p + 2);              // same as a[2]
p++;                   // now points at a[1]

int sum(const int *a, int n);

Example

When you pass an array, C passes the address of its first element, not a copy. The function can therefore change the caller's array, and it needs the length as a separate parameter:

#include <stdio.h>

void double_all(int a[], int n) {
    for (int i = 0; i < n; i++) {
        a[i] = a[i] * 2;
    }
}

int main(void) {
    int scores[3] = {5, 8, 13};
    double_all(scores, 3);
    printf("%d %d %d\n", scores[0], scores[1], scores[2]);
    return 0;
}
Open in compiler

Inside double_all, a is an arrow into main's array. Changing a[i] changes scores itself.

Line by line

  1. double_all takes the array's address, and its length.
  2. The loop runs over indexes 0 to n - 1.
  3. a[i] = a[i] * 2 changes the caller's array, because a points at it.
  4. main passes scores, and its length, 3.
  5. printf shows the doubled values.

Expected result

10 16 26

In the real world

Passing an address instead of copying is why C can hand a million-element array to a function instantly. The standard functions memcpy and qsort work this way, with a pointer and a length.

Common mistakes

  • sizeof on an array parameter gives the size of a pointer (8), not of the array. Pass the length.
  • Moving a pointer past the end of the array and then reading through it.
  • Forgetting that a function can change an array you pass to it.

Best practices

  • Pass the length as a separate parameter.
  • Mark a pointer parameter const if the function only reads through it.
  • Prefer indexes when they are clearer, since a[i] and *(a + i) mean the same thing.

Summary

An array name acts as the address of its first element. Pointer arithmetic moves by whole elements. Functions receive that address, so they can change the array and need its length passed in.

Quick check

1. int a[5]; int *p = a; What is p[3]?
2. If p points to an int at address 1000, where does p + 1 point?
3. What does a function receive when you pass it an array?

Key takeaways

  • p + 1 moves by one element, not one byte.
  • A function given an array can change it.
  • Always pass the length along with the array.

Practice problems for this lesson

My notes

Write down what you want to remember from this lesson. Notes are saved on this device as you type, and appear on your dashboard.