int marks[5] = {
45, 62, 78, 34, 90
}
;Finding the Size of an Array in C: Length Calculation Techniques
What is Finding the Size of an Array in C? Core Concept & Direct Summary
How do you calculate array length using the `sizeof(arr) / sizeof(arr[0])` macro idiom, and why does it fail for function parameters?
Here's a question that looks trivial and isn't: how long is my array?
In some languages you'd write array.length and move on. In C, there's no such property — an array is just a block of memory, and nothing in it records how many elements it holds. No header, no metadata, no hidden count. Just bytes.
So C programmers use a technique, and that technique has one famous failure case that catches essentially every beginner exactly once. This tutorial covers the idiom, the failure, why the failure happens, and the safe patterns to use instead. Get this right and a whole category of "my loop went crazy" bugs disappears from your life.
(Suggested image: the hero graphic — an array of 5 boxes with a measuring tape spanning it, and the formula sizeof(arr) / sizeof(arr[0]) = 20 / 4 = 5 shown below)
How Does Finding the Size of an Array in C Work? In-Depth Mechanics & Architecture
Start With the Core Fact: Arrays Don't Know Their Size
Ask C "how many elements is this?" and the honest answer is: it doesn't know. The array is 20 bytes of stack memory holding five 4-byte integers. Nothing in those bytes says "there are five of me."
The size exists only in the source code — in the declaration [5]. During compilation, the compiler knows. At runtime, the running program does not.
This is why:
- You can't do
marks.lengthormarks.size() - You must pass the count along with the array when handing it to functions
- C relies on conventions (like the
'\0'terminator in strings) whenever it needs to find the end of something at runtime
Now, the good news: at the point where you declared the array, the compiler can still tell you the size — and that's where sizeof comes in.
The Standard Idiom: sizeof(arr) / sizeof(arr[0])
Output:
The logic is beautifully simple:
sizeof(marks)→ the whole array's size in bytes (20 = 5 × 4)sizeof(marks[0])→ one element's size in bytes (4)- Divide → 5
And it works for any element type without you changing a thing:
You never need to know the type's byte size — the division cancels it out. That's why this idiom beats hard-coding 4 or 8.
Note the %zu format specifier — from our format specifiers tutorial. sizeof produces a size_t (an unsigned type), so %zu is the correct specifier. Using %d happens to work on many systems but is technically wrong and warns with -Wformat.
(Suggested image: the division shown visually — a 20-byte array strip being cut into 4-byte chunks, yielding 5 chunks)
Making It Readable: A Macro
Since you'll write this constantly, most codebases define a shorthand:
Then:
Two important cautions about this macro:
- The parentheses are essential.
#define SIZE(a) sizeof(a) / sizeof(a[0])without wrapping, used in an expression liken * SIZE(a), would expand ton * sizeof(a) / sizeof(a[0])— which happens to work here due to left-to-right evaluation of*and/, but the principle still applies: always fully parenthesize macros. The wrapper above is the safe form. - This macro has the exact same limitation as the raw idiom — it uses
sizeof, so it fails anywheresizeoffails. A macro can't magically fix the decay problem (coming next). Many experienced C programmers deliberately avoid this macro precisely because it makes the failure mode look safe when it isn't.
The Famous Failure: It Breaks Inside Functions
Here's the trap. This code looks perfectly reasonable — and it's wrong:
Typical output on a 64-bit system:
Two elements?! Where did 5 go?
The explanation: array-to-pointer decay. When you pass an array to a function, C does not pass the array. It passes a pointer to the first element. Inside printArray, arr is an int * — not an array. So:
sizeof(arr)= size of a pointer = 8 bytes (on 64-bit systems)sizeof(arr[0])= 4 bytes- 8 / 4 = 2 ← the bogus answer
That "2" is pure coincidence, and it's why this bug is so sneaky: sometimes the number looks plausible. On a 32-bit system you'd get 4 / 4 = 1. On other element types you'd get different nonsense. sizeof on a function parameter never gives the array size — only the pointer size.
(Suggested image: the decay diagram — a full array of 5 boxes in main() vs the function receiving a single small pointer box, with the "20 bytes → 8 bytes" label showing where the size information is lost)
Why Does Decay Happen?
Because that's how C was designed. Array parameters are really pointer parameters — void f(int arr[]) and void f(int *arr) are exactly the same declaration to the compiler (this is why our prototypes tutorial showed both forms as equivalent). The bracket syntax is just friendlier notation for "pointer to the first element."
It's an efficiency choice: copying an entire array on every function call would be expensive. Passing an address costs one word.
The Safe Pattern: Pass the Size Explicitly
Since the function can't know the size, you tell it:
Output:
This is the universal C convention: whenever an array goes into a function, its length goes along with it as a separate argument. You'll see this in qsort(), memcpy(), fwrite(), and essentially every standard library function that takes an array. It's not a workaround — it's the idiom.
(Suggested image: "the size travels with the array" — a function signature card showing int arr[], int n with an arrow from main's sizeof calculation)
Sizing Strings: sizeof vs strlen()
Character arrays have their own size confusion, because there are two different "sizes" that matter.
Two totally different numbers, both correct:
strlen walks the characters until it finds the terminating '\0' — which is exactly the runtime convention C uses to make strings self-describing (since arrays themselves aren't). That's why forgetting the '\0' breaks string functions: they run past the end hunting for a terminator that isn't there.
Practical consequences:
- Reserving space: use
sizeof→char line[100];fits up to 99 characters + terminator - Measuring user input: use
strlen→ how many characters did they actually type? - Reading input safely:
fgets(name, sizeof(name), stdin)— thesizeofgives the buffer capacity, which is exactly whatfgetsneeds
That last point is a nice illustration of the difference mattering: fgets wants the capacity (how much room exists), because it's guarding against overflow. It doesn't care about the current text length.
Other Techniques You'll See
Sentinel values (when there's no size to pass)
Some data has a natural "end marker" instead of a count. Strings use '\0'; other arrays might use -1, 0, or a special constant:
This is a sentinel-controlled loop — the same pattern from our while-loop tutorial. It works, but it costs you the ability to store the sentinel value as real data, and skipping the sentinel by accident runs off the end. Prefer an explicit count when you can.
Variables tracking count alongside the array
In practice, most real code stores the count next to the array and never recomputes it:
This handles a common situation the sizeof idiom can't: an array with more capacity than current contents. sizeof(marks) / sizeof(marks[0]) gives you MAX (the room), never the number you've actually filled. When the distinction matters (and it usually does with user input), you track the count yourself.
sizeof on pointers: never the answer
Once you're holding a pointer, sizeof tells you about the pointer, not the array behind it. There's no portable way to recover an array's length from a bare pointer — the information is simply gone. (A gentle preview: this is why the Pointers cluster will keep saying "pass the size with the pointer.")
(Suggested image: a decision flowchart — "Is this an array in its declaring scope? → sizeof works. Is it a function parameter or pointer? → pass the size in. Is it a string? → strlen.")
Putting It All Together: A Safe Array Library
Here's how a small, correct set of array functions looks in practice:
Sample Run:
Three things to notice:
- Every function takes the count —
printArray(marks, count),findMax(marks, count). Nowhere does any of them trysizeofon the parameter. conston the array parameters — it promises the function won't modify your data, which is careful, professional C. The compiler enforces it.- The count variable pattern —
countgrows with each input, handling the "capacity 100, contents 4" distinction thatsizeofalone can't express.
That's the pattern. Write it a few times and array functions will stop feeling like a trap.
Common Size Mistakes
- Using
sizeofon an array parameter inside a function — the big one. You get the pointer size (8 or 4), not the element count. Always pass the count separately. sizeofon a pointer — same trap, different disguise:int *p = arr; sizeof(p)is the pointer, never the array.- Confusing
sizeofandstrlenfor strings — capacity vs content length. The source of endless off-by-one confusion in string code. - Forgetting
%zuwhen printingsizeofresults —%dis technically wrong forsize_tand warns under-Wformat. - Assuming the macro version is magic —
ARRAY_SIZE(arr)is stillsizeof, so it breaks in exactly the same places. Never use it on a parameter. - Expecting to count initialized elements —
sizeof(arr)/sizeof(arr[0])always returns the declared capacity, not how many slots you've filled. Unfilled slots count too (as zeros or garbage). - Trying to recover size from a bare pointer at runtime — impossible in portable C. If you need it later, store it or pass it; don't hope.
(Suggested image: "7 Array Size Mistakes" checklist graphic — consistent with the series)
Final Thoughts
The size of an array in C comes down to one idea: the size lives in the source code, not in the data. Where the compiler can still see your declaration, sizeof(arr) / sizeof(arr[0]) hands you the count with perfect accuracy. The moment the array crosses a function boundary, it becomes a pointer and the size information is gone — so you carry it across yourself, as a parameter.
Everything else follows from that: strlen for strings, a tracking variable for partially filled arrays, sentinels when a marker is the natural design, const int arr[], int n in every array function you write. Get the habit right and you'll never be surprised by a "why did my loop only process 2 elements?" bug again.
Next in this cluster: passing arrays to functions properly — where these size conventions get their full build-out, along with const parameters and multi-file array handling.
Runnable C Code Example & Practical Implementation
#include <stdio.h>
#define ARRAY_SIZE(arr)(sizeof(arr) / sizeof((arr)[0]))
void printSize(int arr[]) {
// WARNING: arr is a pointer here!
printf("Inside function: sizeof(arr) = %zu (Pointer size!)\
", sizeof(arr));
}
int main() {
int data[8] = {
1, 2, 3, 4, 5, 6, 7, 8
};
printf("In main: Array length = %zu\
", ARRAY_SIZE(data));
printSize(data);
return 0;
}Key Takeaways, Best Practices & Memory Safety Rules
sizeof to determine array size on function parameters; always pass size explicitly as a size_t argument.Frequently Asked Questions & Technical Interview Insights
Technical Interview & Viva Questions
Frequently asked C programming interview questionsQ: Why does `sizeof(arr) / sizeof(arr[0])` yield `2` or `1` inside a function on a 64-bit machine?
Q: How do you pass array size portably?
Related Tools, Tutorials & Authoritative References
Recommended Interactive Tools
- Online C Compiler & Runner — Interactive online GCC sandbox to execute C code in real time.
- Online C Code Editor & IDE — Multi-file C programming environment with code formatting.
- C++ Programming Tutorial — Transition from procedural C to Object-Oriented C++ with STL.
- JavaScript Programming Tutorial — Master modern JavaScript fundamentals, DOM APIs, and async patterns.
Authoritative Standards & Citations
- ISO/IEC 9899 (C Language Standard) — Official international C specification by ISO/IEC JTC 1/SC 22/WG 14.
- GNU GCC Documentation — Official reference manual for the GNU Compiler Collection and C libraries.
- The C Programming Language (K&R) — Definitive classic work by Brian Kernighan & Dennis Ritchie at Bell Labs.
- C Reference Documentation (cppreference.com) — Verified reference covering C89 through C23 language features.