Pointers and Arrays
Arrays and pointers are closely linked in C. In most expressions an array's name turns into a pointer to its first element, and a[i] is defined in terms of pointer arithmetic. That's why arrays are passed to functions so cheaply — and why the function loses track of their length.
Array decay
When you use an array's name in an expression, it decays into a pointer to element 0:
#include <stdio.h> int main(void) { int primes[] = { 2, 3, 5, 7, 11 }; int *p = primes; // same as &primes[0] printf("%d %d\n", *p, primes[0]); printf("same address? %s\n", p == &primes[0] ? "yes" : "no"); printf("p[3] = %d\n", p[3]); // a pointer can be indexed like an array return 0; }
2 2 same address? yes p[3] = 7
a[i] means *(a + i)
Indexing is defined as pointer arithmetic: a[i] is exactly *(a + i), "the element i places after the start". Adding to a pointer moves it by whole elements, not bytes (next lesson):
#include <stdio.h> int main(void) { double temps[] = { 18.5, 21.0, 19.25 }; for (int i = 0; i < 3; i++) { printf("%.2f %.2f\n", temps[i], *(temps + i)); } return 0; }
18.50 18.50 21.00 21.00 19.25 19.25
Arrays as function parameters
Because of decay, a function never receives an array — only a pointer. These three declarations are identical:
double average(double values[10], size_t n); double average(double values[], size_t n); double average(double *values, size_t n);
The size in the first form is ignored. Inside the function, sizeof values is the size of a pointer, not the array, and compilers warn if you try:
#include <stdio.h> double average(double values[], size_t n) { printf("sizeof values = %zu\n", sizeof values); double sum = 0; for (size_t i = 0; i < n; i++) sum += values[i]; return sum / n; } int main(void) { double data[] = { 1, 2, 3, 4, 5, 6 }; printf("%.1f\n", average(data, 6)); return 0; }
main.c:4:44: warning: sizeof on array function parameter will return size of 'double *' instead of 'double[]' [-Wsizeof-array-argument]
That's why C functions always take the length as a separate argument (or use a terminator, as strings do).
Where arrays and pointers differ
Array int a[5] | Pointer int *p | |
|---|---|---|
| Storage | Holds 5 ints | Holds one address |
sizeof | 20 (whole array) | 8 (a pointer, on 64-bit systems) |
| Can be reassigned? | No — a = … is an error | Yes — p = other |
& | &a is a pointer to the whole array | &p is the pointer's own address |
#include <stdio.h> int main(void) { int a[5] = { 0 }; int *p = a; printf("sizeof a = %zu, sizeof p = %zu\n", sizeof a, sizeof p); return 0; }
sizeof a = 20, sizeof p = 8
Strings: array or pointer?
The difference matters most with strings:
#include <stdio.h> int main(void) { char word[] = "cat"; // an array: a modifiable copy of the text const char *label = "dog"; // a pointer to a read-only string literal word[0] = 'b'; // fine label = "cow"; // fine: the pointer now points elsewhere printf("%s %s\n", word, label); printf("%zu %zu\n", sizeof word, sizeof label); return 0; }
bat cow 4 8
Passing part of an array
Because a function just gets a pointer, you can pass any slice by pointing into the middle:
#include <stdio.h> int sum(const int *values, size_t n) { int total = 0; for (size_t i = 0; i < n; i++) total += values[i]; return total; } int main(void) { int months[12] = { 5, 8, 6, 9, 12, 15, 18, 17, 13, 10, 7, 6 }; printf("whole year: %d\n", sum(months, 12)); printf("summer (Jun-Aug): %d\n", sum(months + 5, 3)); printf("last quarter: %d\n", sum(&months[9], 3)); return 0; }
whole year: 126 summer (Jun-Aug): 50 last quarter: 23
Try it yourself
Write find, which returns a pointer to the first element equal to a target, or NULL. Use it to change that element in place.
12 20 7 0 3