Kodewick
Lesson 17 of 30Contents

Pointers and arrays

Lesson 17 of 30 · Part 3 · Pointers · about 15 minutes

An array's elements sit side by side in memory, one right after another. That makes pointers and arrays close relatives in C. Most of the time, an array's name works as the address of its first element:

main.c
1#include <stdio.h>2 3int main(void) {4    int scores[4] = {70, 85, 92, 64};5    int *p = scores;6    printf("%d\n", *p);7    printf("%d\n", p == &scores[0]);8    return 0;9}
Output
70
1

int *p = scores; has no &, and none is needed: scores here means &scores[0]. Press Step and you'll see the arrow from p pointing at the first cell of the array.

p + 1 is the next element

Add 1 to a pointer into an array and you get the address of the next element, not the next byte. An int takes 4 bytes, so the address actually goes up by 4; C works that out from the pointer's type.

main.c
1#include <stdio.h>2 3int main(void) {4    int scores[4] = {70, 85, 92, 64};5    int *p = scores;6    printf("%d\n", *(p + 1));7    printf("%d\n", *(p + 3));8    p = p + 2;9    printf("%d\n", *p);10    return 0;11}
Output
85
64
92

Press Step and watch line 8: the arrow moves two cells to the right, to 92. The parentheses in *(p + 1) matter: *p + 1 would be the first element plus one, 71.

p[i] means *(p + i)

In C, square brackets are a short way to write "add, then follow". a[i] means exactly *(a + i). That works for an array name and for a pointer alike:

You writeIt means
a[0]*a
a[i]*(a + i)
&a[i]a + i
main.c
1#include <stdio.h>2 3int main(void) {4    double prices[3] = {2.50, 4.00, 1.25};5    double *p = prices;6    printf("%.2f %.2f\n", prices[2], *(prices + 2));7    printf("%.2f %.2f\n", p[1], *(p + 1));8    p[0] = 3.00;9    printf("%.2f\n", prices[0]);10    return 0;11}
Output
1.25 1.25
4.00 4.00
3.00

So a pointer into an array can be used just like the array. Indexes start at 0 because the index is how far you go from the start: the first element is 0 steps away.

Walking an array with a pointer

Instead of counting with i, you can move a pointer from the first element to the last. scores + 4 is the spot just past the end; stop when p reaches it, before reading anything there.

main.c
1#include <stdio.h>2 3int main(void) {4    int scores[4] = {70, 85, 92, 64};5    int total = 0;6    for (int *p = scores; p < scores + 4; p++) {7        total += *p;8    }9    printf("Total: %d\n", total);10    return 0;11}
Output
Total: 311

Press Step and watch the arrow from p move one cell to the right on each pass, while total grows. p++ moves the pointer to the next element.

Guess first

What does this print?

1#include <stdio.h>2 3int main(void) {4    int steps[5] = {10, 20, 30, 40, 50};5    int *p = steps + 1;6    p++;7    printf("%d %d\n", *p, p[1]);8    return 0;9}

Arrays in functions

When you pass an array to a function, the array isn't copied. The function gets the address of its first element. That's why a function can change the caller's array, and why int a[] and int *a mean the same thing as a parameter:

main.c
1#include <stdio.h>2 3void add_to_all(int *a, int count, int amount) {4    for (int i = 0; i < count; i++) {5        a[i] += amount;6    }7}8 9int main(void) {10    int prices[3] = {10, 25, 40};11    add_to_all(prices, 3, 5);12    printf("%d %d %d\n", prices[0], prices[1], prices[2]);13    return 0;14}
Output
15 30 45

Press Step: inside add_to_all, a is an arrow pointing back into main's array, and the cells change in main's frame.

Since the function only gets an address, it can't know how long the array is. sizeof inside the function gives the size of a pointer, not of the array:

main.c
1#include <stdio.h>2 3void show_size(int a[]) {4    printf("In the function: %zu\n", sizeof a);5}6 7int main(void) {8    int prices[5] = {10, 25, 40, 8, 12};9    printf("In main: %zu\n", sizeof prices);10    show_size(prices);11    return 0;12}
Output
main.c: In function 'show_size':
main.c:4:45: warning: 'sizeof' on array function parameter 'a' will return size of 'int *' [-Wsizeof-array-argument]
    4 |     printf("In the function: %zu\n", sizeof a);
      |                                             ^
main.c:3:20: note: declared here
    3 | void show_size(int a[]) {
      |                ~~~~^~~
In main: 20
In the function: 8

In main, prices is 5 ints, 20 bytes. In the function, a is a pointer, 8 bytes. gcc even warns about it. So pass the count as a separate parameter, the way add_to_all does.

Why == doesn't compare strings

A string is a char array, so its name is the address of its first character too. Lesson 14 promised to explain why typed == saved is false even when the letters match. Now you can see it: == compares the two addresses, and two arrays are always in two different places.

main.c
1#include <stdio.h>2#include <string.h>3 4int main(void) {5    char saved[] = "secret";6    char typed[] = "secret";7    char *same = saved;8    printf("typed == saved: %d\n", typed == saved);9    printf("same == saved: %d\n", same == saved);10    printf("strcmp: %d\n", strcmp(typed, saved));11    return 0;12}
Output
typed == saved: 0
same == saved: 1
strcmp: 0

same points at the same array as saved, so they compare equal. strcmp follows both addresses and compares the characters one by one, which is what you want for text.

Walking a string with a pointer looks like walking any array, except that you stop at '\0' instead of counting:

main.c
1#include <stdio.h>2 3int main(void) {4    char word[] = "banana";5    int count = 0;6    for (char *p = word; *p != '\0'; p++) {7        if (*p == 'a') {8            count++;9        }10    }11    printf("%d\n", count);12    return 0;13}
Output
3

Practice

Write the code, then press Check. A hint costs nothing; the answer is there when you need it.

Exercise 1

Add it up with a pointer

Add up the steps in steps by moving a pointer through the array (don't use steps[i]), and print Total: 25000.

It should print
Total: 25000
exercise1.c
1#include <stdio.h>2 3int main(void) {4    int steps[5] = {4000, 6000, 3000, 7000, 5000};5    int total = 0;6 7    printf("Total: %d\n", total);8    return 0;9}
Output
Press Run or Ctrl+Enter
Exercise 2

Count the passes

count_pass should count the scores of 60 or more, but it works out the count with sizeof inside the function and gets it wrong. Give it a count parameter instead, so the program prints Passed: 4.

It should print
Passed: 4
exercise2.c
1#include <stdio.h>2 3int count_pass(int scores[]) {4    int count = sizeof scores / sizeof scores[0];5    int pass = 0;6    for (int i = 0; i < count; i++) {7        if (scores[i] >= 60) {8            pass++;9        }10    }11    return pass;12}13 14int main(void) {15    int scores[6] = {58, 72, 91, 45, 60, 83};16    printf("Passed: %d\n", count_pass(scores));17    return 0;18}
Output
Press Run or Ctrl+Enter
Exercise 3

Your own strlen

Write my_strlen, which walks the string with a pointer until it reaches '\0' and returns how many characters it passed. The program should print 8.

It should print
8
exercise3.c
1#include <stdio.h>2 3int my_strlen(char *s) {4 5}6 7int main(void) {8    printf("%d\n", my_strlen("keyboard"));9    return 0;10}
Output
Press Run or Ctrl+Enter

What you learned

  • An array's name works as the address of its first element: int *p = scores;.
  • p + 1 is the address of the next element; C moves by the size of the type.
  • a[i] means *(a + i), for arrays and pointers alike.
  • You can walk an array with p++, stopping before the spot just past the end, or at '\0' for a string.
  • An array passed to a function is passed as an address: the function can change it, but sizeof there gives the size of a pointer, so pass the count too.
  • == on two strings compares addresses; compare the text with strcmp.