Pointers and arrays
The pointers lesson showed that an array's name works as a pointer to its first element. This lesson follows that idea further.
Pointer arithmetic
#include <stdio.h>
int main() {
int a[5] = {10, 20, 30, 40, 50};
int *p = a; // the same as &a[0]
printf("%d\n", *p); // 10
printf("%d\n", *(p + 2)); // 30
p++; // move to the next element
printf("%d\n", *p); // 20
p = p + 2; // move two more
printf("%d\n", *p); // 40
return 0;
}Adding 1 to a pointer moves it to the next element, not the next byte. For a pointer to int, that's 4 bytes along: the compiler scales the arithmetic by the size of the type it points to.
a[i] means *(a + i)
#include <stdio.h>
int main() {
int a[4] = {3, 6, 9, 12};
int *p = a;
for (int i = 0; i < 4; i++) {
printf("a[%d]=%d *(a+%d)=%d p[%d]=%d\n", i, a[i], i, *(a + i), i, p[i]);
}
printf("%d\n", &a[2] == a + 2); // 1: the same address
return 0;
}Array indexing is pointer arithmetic: a[i] is defined as *(a + i), "start at a and move i elements along". That's why the first index is 0.
Walking a string with a pointer
#include <stdio.h>
int main() {
char word[] = "pointer";
char *p = word;
while (*p != '\0') {
printf("%c ", *p);
p++;
}
printf("\n");
return 0;
}Instead of counting with an index, the pointer itself moves along the string until it reaches the terminating '\0'. A great deal of classic C code is written in this style.
Arrays passed to functions are pointers
#include <stdio.h>
void doubleAll(int *values, int n) {
for (int i = 0; i < n; i++) {
*(values + i) = *(values + i) * 2; // the same as values[i] *= 2
}
}
int sum(int values[], int n) { // in a parameter list, int values[] means int *values
int total = 0;
for (int i = 0; i < n; i++) total += values[i];
return total;
}
int main() {
int nums[4] = {1, 2, 3, 4};
doubleAll(nums, 4);
printf("%d %d %d %d\n", nums[0], nums[1], nums[2], nums[3]);
printf("sum: %d\n", sum(nums, 4));
return 0;
}Passing an array passes the address of its first element. That's why a function can change the caller's array, and also why it needs the length passed separately: a pointer alone doesn't know how many elements follow it.
The sizeof trap
void show(int values[]) {
printf("%zu\n", sizeof(values)); // 8 on most systems: the size of a pointer
}
int main(void) {
int nums[10];
printf("%zu\n", sizeof(nums)); // 40: the whole array
show(nums);
return 0;
}Inside the function, values is only a pointer, so sizeof reports the size of a pointer. sizeof(array) / sizeof(array[0]) counts elements only where the real array is declared.
Pointer subtraction and one past the end
int a[5] = {10, 20, 30, 40, 50};
int *start = &a[1];
int *end = &a[4];
printf("%d\n", (int) (end - start)); // 3: the number of elements between them
for (int *q = a; q < a + 5; q++) { // stop one past the last element
printf("%d ", *q);
}Subtracting two pointers into the same array gives the number of elements between them, and < tells you which comes first. a + 5 points just past the last element: it's fine to compare against, but must never be dereferenced. (The in-browser C interpreter can't run these two yet; the server's compiler can.)
const and string literals
const char *msg = "Hello"; // the characters can't be changed through msg
msg = "Bye"; // fine: msg itself can point somewhere else
msg[0] = 'J'; // error: the text is read-only
char buffer[] = "Hello"; // an array of your own, which you can change
buffer[0] = 'J'; // fine: now "Jello"A string literal like "Hello" lives in read-only memory. Point at it with a const char *, and copy it into an array when you need to change it.