sizeof Array Decay Trap: The Element Count That Forgets How Many Elements2026-06-08
This function is supposed to print every element of the array it receives. The caller passes an array of five integers, but only two of them show up on the terminal. The code compiles without a warning on many setups, and the formula sizeof(arr)/sizeof(arr[0]) is one most C programmers have typed a thousand times.
#include <stdio.h>
void print_all(int arr[]) {
size_t n = sizeof(arr) / sizeof(arr[0]);
for (size_t i = 0; i < n; i++) {
printf("%d\n", arr[i]);
}
}
int main(void) {
int numbers[] = {10, 20, 30, 40, 50};
print_all(numbers);
return 0;
}
Expected output: five lines, 10 through 50. Actual output on a typical 64-bit Linux box: 10 and 20, and then silence.
The parameter declaration int arr[] looks like an array, but it isn't one. In a function parameter list, C silently rewrites every array type into a pointer to its element type. So void print_all(int arr[]) is exactly identical to void print_all(int *arr) — the brackets are visual decoration with no semantic weight.
That means inside print_all, sizeof(arr) is sizeof(int *) — eight bytes on a 64-bit system, four on a 32-bit one. Divide by sizeof(arr[0]), which is sizeof(int) (four), and you get n == 2. The loop runs twice and stops, no matter how big the array actually was.
This is the array-to-pointer decay rule, and it's one of the oldest traps in C. The same expression sizeof(numbers)/sizeof(numbers[0]) works in main, where numbers is a real array of known size — there sizeof(numbers) is 20. The moment you cross the function boundary, the type information evaporates.
Worse: modern compilers can warn about this (-Wsizeof-array-argument in GCC and Clang), but the warning is not in -Wall on older versions, and the code is perfectly well-defined — it just silently does the wrong thing. There's no UB to catch, no sanitizer to flag it.
You must pass the length explicitly. There is no portable way for the callee to recover the size of the original array:
void print_all(const int *arr, size_t n) {
for (size_t i = 0; i < n; i++) {
printf("%d\n", arr[i]);
}
}
#define ARRAY_LEN(a) (sizeof(a) / sizeof((a)[0]))
int main(void) {
int numbers[] = {10, 20, 30, 40, 50};
print_all(numbers, ARRAY_LEN(numbers));
return 0;
}
The macro ARRAY_LEN only works where a is a true array — apply it to a pointer and you'll get the same nonsense. In C11 and later, you can guard against that with _Static_assert and __builtin_types_compatible_p, refusing to compile when someone hands the macro a pointer. C99 also offers a sharper signature — void print_all(size_t n, int arr[static n]) — which both documents the intent and lets some compilers warn on too-short arrays at the call site.
The deeper lesson: in C, arrays are not first-class values. They can't be passed, returned, or assigned. Every time it looks like you're "passing an array," you're really passing a pointer to its first element — and the size, the cardinal piece of information, is your job to carry alongside it.
int arr[] parameter is a pointer in disguise, so sizeof(arr)/sizeof(arr[0]) silently returns 2 (or 1) instead of the array length — always pass the size explicitly across function boundaries.
