Question
Given this C program:
#include <stdio.h>
int main(void)
{
int days[] = {1, 2, 3, 4, 5};
int *ptr = days;
printf("%zu\n", sizeof days);
printf("%zu\n", sizeof ptr);
return 0;
}
Is there a way to determine the number of elements in the array that ptr points to? sizeof ptr only reports the size of the pointer itself, such as 4 bytes on many 32-bit systems.
Short Answer
In C, an array has a size only while it is still an array object. When its value is assigned or passed as a function argument, it usually becomes a pointer to its first element. A plain pointer does not contain the array length, so the length must be calculated before decay or supplied separately.
Concept
days is an array of five int values. In the scope where it is declared, sizeof days means the total number of bytes occupied by the whole array.
int days[] = {1, 2, 3, 4, 5};
sizeof days /* 5 * sizeof(int) */
sizeof days[0] /* sizeof(int) */
You can therefore calculate its element count:
size_t count = sizeof days / sizeof days[0];
However, in most expressions an array decays into a pointer to its first element. This happens here:
int *ptr = days;
After that assignment, ptr stores only an address. It does not store:
- the number of elements,
- the number of bytes allocated,
- where the array ends, or
- whether the memory came from an array,
malloc, or another source.
Therefore, sizeof ptr is the size of an int *, not the size of days. This distinction matters because C functions often receive pointers; callers must also provide the length when the function needs to traverse the data.
Mental Model
Think of an array as a row of numbered lockers. The complete row knows how many lockers it contains. A pointer is only a note saying, “Start at locker 1.”
From that note alone, you cannot know whether there are 5 lockers, 50 lockers, or none that you are allowed to access. C does not attach the row length to the address.
Syntax and Examples
Calculate an array length where the variable is truly an array:
#include <stddef.h>
int values[] = {10, 20, 30, 40};
size_t length = sizeof values / sizeof values[0];
sizeof valuesis the total size of all elements.sizeof values[0]is the size of one element.- Dividing gives the element count.
A reusable macro is common in C:
#define ARRAY_LEN(array) (sizeof (array) / sizeof (array)[0])
int values[] = {10, 20, 30, 40};
size_t length = ARRAY_LEN(values); /* 4 */
Use this macro only with actual arrays, not pointers:
int *ptr = values;
size_t wrong = ARRAY_LEN(ptr); /* Incorrect: ptr is not an array */
Pass a pointer and its length to a function:
#include <stddef.h>
#include <stdio.h>
void print_numbers(const int *numbers, size_t count)
{
for (size_t i = 0; i < count; i++) {
printf("%d\n", numbers[i]);
}
}
int main(void)
{
int days[] = {1, 2, 3, 4, 5};
print_numbers(days, sizeof days / sizeof days[0]);
return 0;
}
days decays to const int * for the first argument, while the second argument preserves the count calculated at the call site.
Step by Step Execution
Consider this example:
int days[] = {1, 2, 3, 4, 5};
int *ptr = days;
size_t bytes = sizeof days;
size_t count = sizeof days / sizeof days[0];
size_t pointer_bytes = sizeof ptr;
Step by step:
daysis created as an array containing fiveintelements.ptr = daysuses the array's first-element address. It is equivalent toptr = &days[0]in this context.sizeof daysis evaluated using the array type,int[5], so the result is5 * sizeof(int)bytes.- Dividing by
sizeof days[0]removes the bytes-per-element part and produces5. sizeof ptris evaluated from the typeint *. Its result is the storage size of an address on the current platform, not the array size.
For example, if int is 4 bytes and pointers are 8 bytes, bytes is 20, count is 5, and pointer_bytes is 8. Do not assume these particular sizes; C implementations may use different sizes.
Real World Use Cases
- Processing API data: A function receives a buffer plus its count, such as
parse_records(records, record_count). - Binary data: File-reading code tracks both
unsigned char *dataand the number of bytes read. - Sorting: Standard-library
qsortaccepts a base pointer, element count, and element size. - Embedded systems: Drivers commonly receive a byte buffer and its capacity to avoid reading past it.
- Dynamic arrays: Code using
mallocstores the allocated element count in a separate variable or a container structure.
Real Codebase Usage
A common C API explicitly accepts a pointer and length:
int sum(const int *items, size_t count)
{
int total = 0;
for (size_t i = 0; i < count; i++) {
total += items[i];
}
return total;
}
The caller calculates the count only where it still has the array:
int scores[] = {8, 12, 15};
int total = sum(scores, sizeof scores / sizeof scores[0]);
For dynamically allocated memory, retain metadata yourself:
size_t count = 100;
int *items = malloc(count * sizeof *items);
if (items == NULL) {
/* Handle allocation failure. */
}
/* Use items with count. */
free(items);
Larger codebases often define a structure to keep related data together:
struct IntArray {
int *data;
size_t length;
};
This makes it harder to accidentally pass data without its length.
Common Mistakes
Using sizeof on a pointer
void print_items(int *items)
{
size_t count = sizeof items / sizeof items[0]; /* Wrong */
}
Function parameters written as int items[] are also adjusted to int *items, so this is still wrong:
void print_items(int items[]) /* Actually treated as int *items */
{
size_t count = sizeof items / sizeof items[0]; /* Wrong */
}
Pass count as a separate argument instead.
Confusing bytes with element count
sizeof days returns bytes, not necessarily the number of elements. Divide by the size of one element.
Assuming pointer sizes
A pointer is not guaranteed to be 4 or 8 bytes. It depends on the platform and compiler. Use sizeof ptr only when you specifically need the pointer object's size.
Using the wrong printf format
sizeof produces a size_t. Print it with %zu:
printf("%zu\n", sizeof days);
Using %u is not portable because size_t is not required to be unsigned int.
Scanning until a value “looks like the end”
An int array has no automatic end marker. Reading until a guessed value is unsafe unless your format explicitly defines a sentinel and guarantees it is present. Strings are different because C strings conventionally end with \0.
Comparisons
| Expression or approach | What it knows | Can it determine element count? |
|---|---|---|
sizeof array | The complete array type, in the declaring scope | Yes, with sizeof array / sizeof array[0] |
sizeof pointer | Only the pointer type | No |
int values[] function parameter | Adjusted by C to int *values | No |
Pointer plus size_t count | Address and explicit length | Yes |
Null-terminated char * string | Address plus a \0 terminator convention | Yes, via strlen, but it scans the string |
struct containing pointer and length | Address and stored metadata | Yes |
An array and a pointer are related, but they are not the same type. Arrays often convert to pointers, but that conversion loses size information.
Cheat Sheet
int a[] = {1, 2, 3};
size_t bytes = sizeof a; /* Total array bytes */
size_t count = sizeof a / sizeof a[0]; /* 3 elements */
int *p = a;
sizeof p; /* Pointer size only */
#define ARRAY_LEN(a) (sizeof (a) / sizeof (a)[0])
Rules:
- Use the
sizeof array / sizeof array[0]pattern only where the expression is an actual array. - A pointer cannot generally reveal how many elements it points to.
- Pass
pointer + countto functions that process arrays. - Use
size_tfor object sizes and counts. - Print
size_tvalues with%zu. mallocdoes not make the allocation size recoverable through the returned pointer in portable C; store it yourself.
FAQ
Can C determine an array's size from an int * pointer?
No. A plain int * contains an address, not an array length. It could point to one element, an array, allocated memory, or invalid memory.
Why does sizeof work for an array but not inside a function?
When an array is passed to a function, it normally converts to a pointer. Inside the function, the parameter has pointer type, so its original array bound is unavailable.
Does int arr[] preserve the array size in a function parameter?
No. In a function parameter list, int arr[] means the same thing as int *arr for this purpose.
Can malloc tell me how many elements were allocated?
Not portably through the returned pointer. Keep the requested count in a size_t variable or a structure.
Why can strlen find a string length from a pointer?
C strings use a convention: a terminating null character ('\0') marks the end. strlen scans until that terminator. Ordinary arrays have no equivalent built-in marker.
Should I use sizeof(*ptr) to get the array size?
No. sizeof *ptr gives the size of one pointed-to element, such as one int. It cannot tell you the number of elements.
Can a variable-length array retain its size?
Yes, while it remains an array in the same scope, sizeof can use its runtime size. But once passed as an ordinary pointer, you should still pass the length separately.
Mini Project
Description
Build a small function that calculates the average of integer readings. The project demonstrates the standard C pattern for safely processing an array: calculate its length at the caller, then pass both the pointer and the length to the function.
Goal
Calculate and print the average of a fixed array without trying to infer its length from a pointer.
Requirements
- Create an array containing at least five integer readings.
- Write a function that accepts a pointer to
intvalues and asize_tcount. - Return
0.0when the count is zero. - Calculate the average without accessing beyond
countelements. - Calculate the array length in
mainusingsizeof.
Keep learning
Related questions
2D Array Loop Order and Cache Performance in C
Learn why swapping nested loops changes 2D array performance in C, using row-major memory layout, cache locality, and practical benchmarks.
Array-to-Pointer Conversion in C and C++ Explained
Learn what array-to-pointer conversion means in C and C++, how array decay works, and how it differs from a pointer to an array.
Building More Fault-Tolerant Embedded C++ Applications for Radiation-Prone ARM Systems
Learn practical C++ and compile-time techniques to reduce soft-error damage in embedded ARM systems exposed to radiation.