Question
C Pointer to Array vs Array of Pointers: How to Read Complex Declarations
Question
What is the difference between the following C declarations?
int *arr1[8];
int (*arr2)[8];
int *(arr3[8]);
Also, what is the general rule for understanding more complex C declarations?
Short Answer
By the end of this page, you will understand how to tell apart an array of pointers from a pointer to an array in C. You will also learn a reliable rule for reading complex C declarations, so syntax like int *arr1[8] or int (*arr2)[8] becomes much easier to decode.
Concept
C declarations can look confusing because symbols like *, [], and () combine in ways that change meaning.
The key idea is that arrays and pointers are different types, even though they are often related.
Consider these declarations:
int *arr1[8];
int (*arr2)[8];
int *(arr3[8]);
What each one means
int *arr1[8];
arr1 is an array of 8 elements, where each element is a pointer to int.
arr1[0]is anint *arr1[1]is anint *- and so on
int (*arr2)[8];
arr2 is a pointer to an array of 8 integers.
arr2points to one whole array*arr2is the actual array of 8int(*arr2)[0]is the firstintin that array
int *(arr3[8]);
This is the same as:
int *arr3[8];
So arr3 is also an array of 8 pointers to int.
The parentheses around arr3[8] do not change the meaning here because [] already binds tightly.
Why this matters
In real C programs, you often work with:
- arrays of strings:
char *names[10] - tables of row pointers
- pointers to fixed-size buffers
- function pointers and callback tables
If you cannot read declarations confidently, it becomes hard to understand APIs, debug type errors, or pass data correctly between functions.
Core truth to remember
int *a[8]means array first, then pointer → array of pointersint (*a)[8]means pointer first because parentheses force it → pointer to array
Mental Model
Think of C declarations like reading a road sign from the variable name outward.
Start at the variable name, then look:
- to the right first if possible
- then to the left
- use parentheses as barriers that group things together
Analogy
Imagine the variable name is a person in the middle of a maze.
- If the first thing they touch is
[], they are an array. - If the first thing they touch is
*, they are a pointer. - Parentheses can change which path they must take first.
For example:
int *arr1[8];
Start at arr1:
- right:
[8]→arr1is an array of 8 - left:
*→ whose elements are pointers - base type:
int
So: array of 8 pointers to int.
Now:
int (*arr2)[8];
Start at arr2:
- immediate left is inside parentheses:
*→arr2is a pointer - then move out right:
[8]→ pointer to an array of 8 - base type:
int
So: pointer to array of 8 int.
Syntax and Examples
Core syntax
int *a[8]; // array of 8 pointers to int
int (*b)[8]; // pointer to array of 8 int
Example 1: Array of pointers
#include <stdio.h>
int main(void) {
int x = 10, y = 20, z = 30;
int *arr[3] = { &x, &y, &z };
printf("%d\n", *arr[0]);
printf("%d\n", *arr[1]);
printf("%d\n", *arr[2]);
return 0;
}
arr is an array. Each element stores the address of an int.
arr[0]isint **arr[0]is theintvalue stored at that address
Example 2: Pointer to an array
#include <stdio.h>
int main(void) {
int values[3] = { 10, 20, 30 };
int (*ptr)[3] = &values;
printf("%d\n", (*ptr)[0]);
printf("%d\n", (*ptr)[1]);
printf("%d\n", (*ptr)[2]);
return 0;
}
Here:
valuesis an array of 3int&valuesis a pointer to the whole arrayptrstores that address(*ptr)[1]gets the second element
Why parentheses matter
Compare:
int *a[5];
int (*b)[5];
These are completely different types.
ais an array containing 5 pointersbis one pointer that points to one array containing 5 integers
Step by Step Execution
Consider this example:
#include <stdio.h>
int main(void) {
int numbers[4] = { 5, 10, 15, 20 };
int (*p)[4] = &numbers;
printf("%d\n", (*p)[2]);
return 0;
}
Step-by-step
1. Declare an array
int numbers[4] = { 5, 10, 15, 20 };
numbers is an array of 4 integers.
2. Declare a pointer to the whole array
int (*p)[4] = &numbers;
Read it from p:
*means pointer[4]means to an array of 4intmeans array elements are integers
So p points to the whole array numbers.
3. Access the third element
(*p)[2]
Break it down:
pis a pointer to the array*pis the array itself(*p)[2]is element at index 2- value is
15
Compare with an array of pointers
#include <stdio.h>
int main(void) {
int a = 5, b = 10, c = 15, d = 20;
int *p[4] = { &a, &b, &c, &d };
printf("%d\n", *p[2]);
return 0;
}
Here:
p[2]is a pointer toc*p[2]is the value15
The output may match, but the types and memory layout are different.
Real World Use Cases
Where array-of-pointers is used
List of strings
char *names[3] = { "Alice", "Bob", "Carol" };
Each element points to the first character of a string.
Sparse or flexible row storage
You can store pointers to separately allocated rows in a table-like structure.
int *rows[10];
Each row can point to a different memory block.
Lookup tables
An array of pointers is useful when each entry refers to another object or buffer.
Where pointer-to-array is used
Passing fixed-size arrays to functions
void print_row(int (*row)[8]);
This says the function expects a pointer to an array of 8 integers.
Working with multidimensional arrays
int matrix[5][8];
int (*row)[8] = matrix;
Each increment of row moves by one full row of 8 integers.
Buffer processing with known dimensions
When a function must preserve row width, a pointer to array is often safer and clearer than flattening everything manually.
Real Codebase Usage
In real codebases, developers use these forms to make intent explicit.
Common patterns
1. Function parameters for fixed-width rows
void process_rows(size_t count, int (*rows)[8]) {
for (size_t i = 0; i < count; i++) {
printf("%d\n", rows[i][0]);
}
}
This is common with matrices, image data, and protocol packets.
2. Arrays of pointers for dynamic collections
char *errors[5];
Useful when each element may point to a different string or object.
3. Validation and guard clauses
void print_first(int (*arr)[8]) {
if (arr == NULL) {
return;
}
printf("%d\n", (*arr)[0]);
}
Pointer checks are common before dereferencing.
4. APIs that distinguish contiguous memory from indirect storage
int (*p)[8]suggests one contiguous block shaped like an array of 8intint *p[8]suggests 8 independent pointers, possibly to unrelated memory
That distinction matters for correctness, performance, and iteration logic.
Common Mistakes
1. Assuming arrays and pointers are always the same
They are related, but not identical types.
Broken thinking:
int *a[8];
int (*b)[8];
b = a; // wrong: incompatible types
Why it fails:
ais an array of pointersbis a pointer to an array of int
2. Ignoring parentheses
int *a[8]; // array of pointers
int (*a)[8]; // pointer to array
A small change in parentheses creates a different type.
3. Dereferencing the wrong thing
Broken example:
int values[3] = {1, 2, 3};
int (*p)[3] = &values;
printf("%d\n", *p[1]);
Why this is wrong:
p[1]means move to the next array of 3 integers- that goes past the one array you actually have
Correct:
printf("%d\n", (*p)[1]);
4. Confusing &array with array
int values[3];
valuesoften behaves like pointer to first element:int *&valuesis pointer to whole array:int (*)[3]
These are not the same type.
5. Reading declarations left to right only
That approach breaks quickly in C.
Instead:
- start at the variable name
- go right when possible
- then go left
- respect parentheses
Comparisons
| Declaration | Meaning | Typical use |
|---|---|---|
int *a[8] | array of 8 pointers to int | list of addresses, strings, rows |
int (*a)[8] | pointer to array of 8 int | fixed-size arrays, matrix rows |
int a[8] | array of 8 int | direct storage of integers |
int *a | pointer to int | points to one int or first element |
array of pointers vs pointer to array
| Feature | Array of pointers | Pointer to array |
|---|---|---|
| Example | int *a[8] | int (*a)[8] |
| Number of pointers stored | 8 | 1 |
| Underlying structure | 8 separate pointer elements | one pointer to one whole array |
| Memory layout | pointers may refer to unrelated places | refers to contiguous array block |
| Access pattern | *a[i] | (*a)[i] |
Equivalent declarations
These mean the same thing:
int *arr1[8];
int *(arr3[8]);
Parentheses around arr3[8] do not change the binding in this case.
Cheat Sheet
Quick reading rule
Start at the variable name:
- Look right first
- Then look left
- Parentheses control grouping
[]and()bind tighter than*
Common patterns
int *a[8]; // array of 8 pointers to int
int (*a)[8]; // pointer to array of 8 int
int (*f)(void); // pointer to function returning int
int *f(void); // function returning pointer to int
Your declarations
int *arr1[8]; // array of 8 pointers to int
int (*arr2)[8]; // pointer to array of 8 int
int *(arr3[8]); // same as int *arr3[8]
Important precedence
[]has high precedence()has high precedence*has lower precedence
So:
int *a[8];
is read as:
a[8]first- then
*
Useful type facts
int values[8];
int *p1 = values; // pointer to first element
int (*p2)[8] = &values; // pointer to whole array
Edge case to remember
array and &array may have the same address value, but they have different types:
values // type like int * in many expressions
&values // type int (*)[8]
FAQ
What is the difference between int *a[8] and int (*a)[8] in C?
int *a[8] is an array of 8 pointers to int. int (*a)[8] is a pointer to one array of 8 integers.
Are int *arr1[8] and int *(arr3[8]) the same?
Yes. They mean the same thing: an array of 8 pointers to int.
Why do parentheses matter in C declarations?
Parentheses change grouping. Since [] binds more tightly than *, parentheses can force * to apply first.
How do I read complex C declarations correctly?
Start at the variable name, read to the right when possible, then to the left, and use parentheses to determine grouping.
Is an array the same as a pointer in C?
No. Arrays and pointers are different types. Arrays often decay to pointers in expressions, but they are not identical.
What type is &arr if arr is int arr[8]?
Its type is int (*)[8], which means pointer to array of 8 integers.
When should I use a pointer to an array?
Use it when you want to refer to a whole fixed-size array, especially for multidimensional arrays or APIs with known dimensions.
Mini Project
Description
Build a small C program that demonstrates the difference between an array of pointers and a pointer to an array. This helps you see that similar-looking declarations can represent very different memory structures and access patterns.
Goal
Create and run a program that prints values using both int *arr[3] and int (*ptr)[3], and observe how access syntax differs.
Requirements
- Declare an array of 3 integers and a pointer to that array.
- Declare three separate integers and an array of 3 pointers to them.
- Print all values from both structures.
- Use the correct syntax for each case:
(*ptr)[i]and*arr[i].
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.