Pointers and arrays
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:
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}
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.
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}
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 write | It means |
|---|---|
a[0] | *a |
a[i] | *(a + i) |
&a[i] | a + i |
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}
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.
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}
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.
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}
steps + 1 points at 20, and p++ moves it on to 30, so *p is 30. p[1] is one element further from where p is now: 40.
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:
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}
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:
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}
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.
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}
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:
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}
3
Practice
Write the code, then press Check. A hint costs nothing; the answer is there when you need it.
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.
Total: 25000
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}
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.
Passed: 4
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}
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.
8
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}
What you learned
- An array's name works as the address of its first element:
int *p = scores;. p + 1is 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
sizeofthere gives the size of a pointer, so pass the count too. ==on two strings compares addresses; compare the text withstrcmp.