Need Help or More Information?
Questions about this free C programming course, embedded C training or career guidance? Get in touch.
Dereferencing Pointer to Array
A free c tutorial on what *ptr really returns when ptr points to an array, with sizes, array decay and a fixed-size buffer demo you can run today.
What You Will Learn
- What you get when you dereference a pointer to an array, and why it is the array itself
- How the array result decays into a pointer, and why
sizeofstill sees the full size - Two correct ways to read an element:
(*whole)[i]andwhole[0][i] - Why
*whole[i]is a trap - How to pass a fixed-size buffer to a function so the compiler checks its length
Prerequisites
- The previous lecture on declaring a pointer to an array
- Basic dereferencing:
*pgives the objectppoints to - GCC on Linux or an embedded ARM toolchain
What Dereferencing a Pointer to an Array Gives You
This free c tutorial answers one question that confuses almost every learner: if whole points to an array of five integers, what is *whole? The rule is the same as for any pointer. Dereferencing a pointer to type T gives you an object of type T. Here T is int[5], so *whole is the five-integer array itself, not a single number.
An array that appears in an expression is converted to a pointer to its first element. That conversion is called decay, and it explains why *whole behaves like an address even though its type is an array. Dereference once more and you finally arrive at an integer.
| Expression | Type | Meaning | sizeof (64-bit) |
|---|---|---|---|
whole | int (*)[5] | Pointer to the whole array | 8 |
*whole | int[5] | The array; decays to the first element’s address | 20 |
**whole | int | The first element | 4 |
(*whole)[2] | int | The third element | 4 |
sizeof Is the Exception to Decay
Decay happens almost everywhere an array is used, but not when the array is the operand of sizeof or of the address operator &. That is why sizeof *whole gives 20, the full array, even though a plain use of *whole in an expression would behave like a pointer. It is also why *whole and samples compare equal when you cast both to void *: they name the same first byte.
Reading Elements Through a Pointer to an Array
There are two clean spellings for element i. The first applies the star to whole to get the array, then subscripts it: (*whole)[i]. The second treats whole like a pointer to the first of several arrays and takes array number zero: whole[0][i]. Both give the same result, and the second form is the one that generalises to many arrays, which is the key to two-dimensional tables in later lectures.
The parentheses trap
*whole[i] does not mean the same as (*whole)[i]. The subscript binds first, so whole[i] moves forward by i whole arrays (20 bytes each), and the star is applied afterwards. For any i other than zero you read memory outside the array. Always write the parentheses.
Demo: Dereferencing and Sizes in Action
Every free c tutorial should let you run the code, so here is a complete program. It reads elements in both styles, compares addresses, prints the three sizes from the table, and then passes a buffer to a function that expects a fixed-size array. That last part previews the practical payoff.
#include <stdio.h>
#include <string.h>
#include <stdint.h>
static void clear_frame(uint8_t (*frame)[8])
{
memset(*frame, 0, sizeof *frame);
}
int main(void)
{
int samples[5] = {3, 5, 6, 7, 9};
int (*whole)[5] = &samples;
printf("(*whole)[2] = %d\n", (*whole)[2]);
printf("whole[0][3] = %d\n", whole[0][3]);
printf("*whole == samples : %s\n", (void *)*whole == (void *)samples ? "yes" : "no");
printf("sizeof(whole) = %zu\n", sizeof whole);
printf("sizeof(*whole) = %zu\n", sizeof *whole);
printf("sizeof(**whole) = %zu\n", sizeof **whole);
uint8_t frame[8] = {1, 2, 3, 4, 5, 6, 7, 8};
clear_frame(&frame);
printf("frame[0]=%u frame[7]=%u\n", frame[0], frame[7]);
return 0;
}
gcc -std=c11 -Wall -Wextra -o e22 e22.c
./e22
Expected output on a 64-bit Linux machine:
(*whole)[2] = 6
whole[0][3] = 7
*whole == samples : yes
sizeof(whole) = 8
sizeof(*whole) = 20
sizeof(**whole) = 4
frame[0]=0 frame[7]=0
Walk through it line by line. The first two lines show both spellings reaching the third and fourth elements of {3, 5, 6, 7, 9}. The comparison line proves the array and the array pointer’s target start at the same byte. The three sizes match the table: 8 for the pointer, 20 for the array it points to, and 4 for one element. The final line shows that clear_frame zeroed all eight bytes.
Passing Fixed-Size Buffers to Functions
This part of the free c tutorial is where the theory pays off. The usual way to hand a buffer to a function is a pointer plus a length. It works, but the compiler cannot tell whether the length you pass is right. A pointer to an array moves the size into the type, so the check happens at build time. In clear_frame above, sizeof *frame is 8 because the parameter type says so; no length argument can go stale.
| Approach | Compiler checks the size? | Typical cost |
|---|---|---|
void f(uint8_t *buf, size_t len) | No, the length is trusted | One extra argument |
void f(uint8_t (*buf)[8]) | Yes, a wrong array length is a type error | Caller must write &frame |
void f(uint8_t buf[8]) | No, it is rewritten to uint8_t *buf | Looks safe but is not |
Try passing the wrong size to see the protection working:
uint8_t small[4] = {1, 2, 3, 4};
clear_frame(&small); /* clear_frame expects uint8_t (*)[8] */
GCC 13 reports it like this (abridged):
warning: passing argument 1 of 'clear_frame' from incompatible pointer type [-Wincompatible-pointer-types]
note: expected 'uint8_t (*)[8]' but argument is of type 'uint8_t (*)[4]'
A four-byte buffer is rejected at compile time. With the pointer-plus-length style the same mistake would build cleanly and overwrite four bytes of whatever sits after the buffer.
Free C Tutorial: Common Dereferencing Mistakes
| Mistake | Symptom | Fix |
|---|---|---|
Writing *whole[i] for (*whole)[i] | Wrong values or reads far outside the array | Always parenthesise the star |
Expecting *whole to be an int | Type mismatch warning in printf("%d", *whole) | Dereference twice, or index with (*whole)[0] |
Using sizeof(whole) to get the array size | Prints 8 instead of 20 | Use sizeof *whole |
Forgetting & at the call site | Incompatible pointer type warning | Pass &frame |
| Casting away the warning | Hidden length mismatch bugs | Fix the type instead of casting |
Free C Tutorial Best Practices and Security
- Use
sizeof *ptrinstead of repeating the length, so the number cannot drift away from the type. - Write
(*whole)[i]with parentheses every time; add a short comment if a colleague might misread it. - Use a typedef for frame types shared across files, and keep the macro that defines the length in one header.
- Compile with
-Wall -Wextraand treat-Wincompatible-pointer-typesas an error.
This free c tutorial would be incomplete without a word on cost. For performance, dereferencing a pointer to an array costs nothing extra; the compiler resolves the decay at build time and emits the same load instruction as for a plain element pointer. For security, remember that the fixed length lives in the type only. If the pointer itself is wrong, or an index is out of range, the compiler cannot help, so validate indexes that come from the outside world such as a received packet.
Real-World Uses
- Protocol frames: a CAN driver exposes
send(uint8_t (*payload)[8])so every caller supplies exactly the classic CAN data size. - Block processing: an audio routine takes
int16_t (*block)[64]and can usesizeof *blockfor its loop bounds. - Row access in tables: a pointer to one row of a keypad map lets you scan columns without copying the data.
Free C Tutorial Key Takeaways and Conclusion
- Dereferencing a pointer to an array gives the array, which decays to the address of its first element.
sizeofand&are the two places where an array does not decay.- Read elements with
(*whole)[i]orwhole[0][i], never*whole[i]. - Taking a pointer to a fixed-size array as a parameter lets the compiler reject wrong-sized buffers.
That wraps up the dereferencing story. If you are following this free c tutorial from the start, you now understand every level between a pointer to an array and the integer inside it. Together with the free embedded c course lectures on pointers and arrays, this gives you the vocabulary to read driver code that passes buffers around by type. The next lecture applies the same idea to two-dimensional arrays, where every row is itself an array.
Frequently Asked Questions
What does *whole return when whole points to an array?
It returns the array itself, whose type is int[5]. In most expressions that array decays to a pointer to its first element.
Why does sizeof *whole give 20?
Because sizeof is one of the places where an array does not decay. It reports the size of the full array, five ints of four bytes each.
Are (*whole)[2] and whole[0][2] the same?
Yes. Both read the third element. The second form treats whole as a pointer to the first of several arrays and selects array number zero.
What is wrong with *whole[2]?
The subscript applies first, so it steps forward two whole arrays and then dereferences, reading memory outside samples.
Why not just pass a pointer and a length?
You can, but the compiler cannot verify that the length matches the buffer. A pointer to a fixed-size array makes the length part of the type and checks it at build time.
Does void f(uint8_t buf[8]) enforce the size?
No. The compiler rewrites that parameter to uint8_t *buf, so any buffer is accepted and sizeof buf is just the pointer size.
Is decay a run-time operation?
No. It is a compile-time conversion, so it adds no instructions beyond the address calculation you would write by hand.
Next Up in the Free C Tutorial
Two-dimensional arrays are arrays of arrays. Continue the free c programming course to see how they sit in memory.
Need Help or More Information?
Questions about this free C programming course, embedded C training or career guidance? Get in touch.

1 Comment