The correct answer is:

int* arr[MAX];
int* (*pArr)[MAX] = &arr;

Or just:

        int* arr  [MAX];
typedef int* arr_t[MAX];

arr_t* pArr = &arr;

The last part reads as "pArr is a pointer to array of MAX elements of type pointer to int".

In C the size of array is stored in the type, not in the value. If you want this pointer to correctly handle pointer arithmetic on the arrays (in case you'd want to make a 2-D array out of those and use this pointer to iterate over it), you - often unfortunately - need to have the array size embedded in the pointer type.

Luckily, since C99 and VLAs (maybe even earlier than C99?) MAX can be specified in run-time, not compile time.

Answer from Kos on Stack Overflow
๐ŸŒ
Reddit
reddit.com โ€บ r/c_programming โ€บ an array of pointers vs a pointer to an array
r/C_Programming on Reddit: An array of pointers vs a pointer to an array
February 1, 2021 -

I've been reading K&R and the syntax that differentiates an array of pointers vs a pointer to an array is confusing me. They say that

int *array[100];

is an array of 100 pointers to integers. On the other hand,

int (*array)[100];

is a pointer to an array of 100 integers.

Can someone elaborate on why this is the case?

It seems to me that it should be the other way around, since *(array[100]) reads like a pointer to an array with 100 elements, while (*array)[100] looks very much like it should be an array of 100 pointers.

What am I missing here?

Top answer
1 of 4
7
Declarations in C are written to match their usage. So if you write int *array[100], this means array has type such that *array[100] is of type int. (Ignoring, of course, that 100 is an invalid array index!) So to determine the type of array, we can use the operator precedence rules. Array indexing is higher precedence than dereferencing, so *array[100] means that we first get index into an array, and then dereference the object we get out, and that all should result in an int. This means that array is an array of pointers to int. (*array)[100] reverses this. Now, it says if we dereference array, and then index into whatever we get out as an array, we get an int. Thus, it's a pointer to an array of ints. Lots of people try to explain this in terms of the 'right-left rule' or the 'spiral rule' or whatever - I find these just make things harder. It's all operator precedence.
2 of 4
5
What you're missing is probably the worst feature of C, and possibly the worst feature of any language, which is its confusing, convoluted type syntax. It doesn't read left to right, or right to left, but inside out. To try and make sense of it, it was supposed to mirror actual usage in an expression: *array[i] # parsed as *(array[i]), index first # then deref, so an array of pointers (*array)[i] # deref first then index, so pointer to array However, here C throws another curve ball: because derefs, derefs with offsets, and array indexing are all really the same thing, then whatever the declaration of array, either of these will work with no error! Good luck...
๐ŸŒ
Medium
medium.com โ€บ @Dev_Frank โ€บ pointer-to-an-array-26e481f8deb0
POINTER TO AN ARRAY. Array of pointers | by Dev Frank | Medium
February 23, 2024 - The program then demonstrates the difference by accessing the 0th element using ptrToElement and printing the entire array using ptrToArray. It then uses ptrToArray to access and print each element of the array in a loop. The key point is that these two pointers have different purposes and should be used accordingly based on the intended operation.
๐ŸŒ
Reddit
reddit.com โ€บ r/c_programming โ€บ casting to a pointer to array of pointers, what is going on?
r/C_Programming on Reddit: Casting to a pointer to array of pointers, what is going on?
October 9, 2021 -
void f(int *a) {
	void *p = &a;
	***(int*(*)[])p = 1;
}

This code is from a tweet. It's probably something simple, but I don't understand what's going on.

I tried finding it on the web, no luck. I don't see any special behaviour for casting to an array in the C89 standard draft either.

Can anyone explain me what's going on? It seems to set *a to 1.

Thanks in advance!

Edit: this is the tweet I'm talking about.

Top answer
1 of 7
12
Your question is already answered, but I though I could give my reasoning as well: First thing to do is figure out what int*(*)[] means. If it were a declaration it would look like int *(*x)[] Then you have to think about which kind of type x needs to have such that expression *(*x)[0] has integer type (declaration follows usage). And that would be a pointer to an array of pointers to integers, since postfix operators have higher precedence than prefix operators. Now we take a look at p: void *p = &a; This is a pointer to a pointer to an integer. ***p is nonsense. So something is happening with the cast to int*(*)[]. Both p and int*(*)[] are pointers, so this cast makes sense but we have to be careful about UB. Let int *(*x)[] = p Then, the final expression looks like ***x = 1; What is *x? Well, it is simply an array of pointers to integers. A rule of C is that arrays almost always decay into pointers (6.3.2.1p3 in the standard): Except when it is the operand of the sizeof operator, or the unary & operator, or is a string literal used to initialize an array, an expression that has type โ€œarray of typeโ€ is converted to an expression with type โ€œpointer to typeโ€ that points to the initial element of the array object and is not an lvalue. Therefore *x will decay to a pointer to the first element of the array. And the *x inside ***x will have type pointer to pointer to integer. Since the pointer has not moved around, but merely changed type, it will still point to the same place as a. Thus ***x = 1, means the same as *a = 1. Generally casting a pointer to an arbitrary pointer type, and back again is fine, as long as the alignment is correct. But I'm unsure about how this interacts with the array decaying. This could be UB, but I'm not convinced either way.
2 of 7
6
I see that as breaking aliasing rules. I believe it's UB.
๐ŸŒ
Aticleworld
aticleworld.com โ€บ home โ€บ difference between pointer to an array and array of pointers
Difference between pointer to an array and array of pointers - Aticleworld
March 13, 2020 - I am assigning the address of the array and address of the first element to the pointers. Now in the last, I am displaying the stored address by the pointers before and after performing increment operation. So the output will be, p = 0x0061FF04, ptr = 0x0061FF04 p = 0x0061FF08, ptr = 0x0061FF18 ยท Because the base type of p is int while the base type of ptr is โ€˜an array of 5 integersโ€™. We know that the pointer arithmetic is performed relative to the base size, so if we write ptr++, then the pointer ptr will be shifted forward by 20 bytes.
๐ŸŒ
W3Schools
w3schools.com โ€บ c โ€บ c_pointers_arrays.php
C Pointers and Arrays
Well, in C, the name of an array, is actually a pointer to the first element of the array.
Find elsewhere
๐ŸŒ
GeeksforGeeks
geeksforgeeks.org โ€บ c language โ€บ pointer-array-array-pointer
Pointer to an Array | Array Pointer - GeeksforGeeks
A pointer to an array is a pointer that points to the whole array instead of the first element of the array.
Published: April 30, 2025
๐ŸŒ
BYJUS
byjus.com โ€บ gate โ€บ difference-between-pointer-to-an-array-and-array-of-pointers
Find the Difference Between Pointer to an Array and ...
March 29, 2023 - But let us first know a bit more about each of them individually. The array pointer is an alternative name to a pointer to an array. We generally make use of this pointer for accessing the various components of any given array.
Top answer
1 of 4
112

Allocated Array

With an allocated array it's straightforward enough to follow.

Declare your array of pointers. Each element in this array points to a struct Test:

struct Test *array[50];

Then allocate and assign the pointers to the structures however you want. Using a loop would be simple:

array[n] = malloc(sizeof(struct Test));

Then declare a pointer to this array:

                               // an explicit pointer to an array 
struct Test *(*p)[] = &array;  // of pointers to structs

This allows you to use (*p)[n]->data; to reference the nth member.

Don't worry if this stuff is confusing. It's probably the most difficult aspect of C.


Dynamic Linear Array

If you just want to allocate a block of structs (effectively an array of structs, not pointers to structs), and have a pointer to the block, you can do it more easily:

struct Test *p = malloc(100 * sizeof(struct Test));  // allocates 100 linear
                                                     // structs

You can then point to this pointer:

struct Test **pp = &p

You don't have an array of pointers to structs any more, but it simplifies the whole thing considerably.


Dynamic Array of Dynamically Allocated Structs

The most flexible, but not often needed. It's very similar to the first example, but requires an extra allocation. I've written a complete program to demonstrate this that should compile fine.

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

struct Test {
    int data;
};

int main(int argc, char **argv)
{
    srand(time(NULL));

    // allocate 100 pointers, effectively an array
    struct Test **t_array = malloc(100 * sizeof(struct Test *));

    // allocate 100 structs and have the array point to them
    for (int i = 0; i < 100; i++) {
        t_array[i] = malloc(sizeof(struct Test));
    }

    // lets fill each Test.data with a random number!
    for (int i = 0; i < 100; i++) {
        t_array[i]->data = rand() % 100;
    }

    // now define a pointer to the array
    struct Test ***p = &t_array;
    printf("p points to an array of pointers.\n"
       "The third element of the array points to a structure,\n"
       "and the data member of that structure is: %d\n", (*p)[2]->data);

    return 0;
}

Output:

> p points to an array of pointers.
> The third element of the array points to a structure,
> and the data member of that structure is: 49

Or the whole set:

for (int i = 0; i < 100; i++) {
    if (i % 10 == 0)
        printf("\n");
    printf("%3d ", (*p)[i]->data);
}

 35  66  40  24  32  27  39  64  65  26 
 32  30  72  84  85  95  14  25  11  40 
 30  16  47  21  80  57  25  34  47  19 
 56  82  38  96   6  22  76  97  87  93 
 75  19  24  47  55   9  43  69  86   6 
 61  17  23   8  38  55  65  16  90  12 
 87  46  46  25  42   4  48  70  53  35 
 64  29   6  40  76  13   1  71  82  88 
 78  44  57  53   4  47   8  70  63  98 
 34  51  44  33  28  39  37  76   9  91 

Dynamic Pointer Array of Single-Dynamic Allocated Structs

This last example is rather specific. It is a dynamic array of pointers as we've seen in previous examples, but unlike those, the elements are all allocated in a single allocation. This has its uses, most notable for sorting data in different configurations while leaving the original allocation undisturbed.

We start by allocating a single block of elements as we do in the most basic single-block allocation:

struct Test *arr = malloc(N*sizeof(*arr));

Now we allocate a separate block of pointers:

struct Test **ptrs = malloc(N*sizeof(*ptrs));

We then populate each slot in our pointer list with the address of one of our original array. Since pointer arithmetic allows us to move from element to element address, this is straight-forward:

for (int i=0;i<N;++i)
    ptrs[i] = arr+i;

At this point the following both refer to the same element field

arr[1].data = 1;
ptrs[1]->data = 1;

And after review the above, I hope it is clear why.

When we're done with the pointer array and the original block array they are freed as:

free(ptrs);
free(arr);

Note: we do NOT free each item in the ptrs[] array individually. That is not how they were allocated. They were allocated as a single block (pointed to by arr), and that is how they should be freed.

So why would someone want to do this? Several reasons.

First, it radically reduces the number of memory allocation calls. Rather then N+1 (one for the pointer array, N for individual structures) you now have only two: one for the array block, and one for the pointer array. Memory allocations are one of the most expensive operations a program can request, and where possible, it is desirable to minimize them (note: file IO is another, fyi).

Another reason: Multiple representations of the same base array of data. Suppose you wanted to sort the data both ascending and descending, and have both sorted representations available at the same time. You could duplicate the data array, but that would require a lot of copying and eat significant memory usage. Instead, just allocate an extra pointer array and fill it with addresses from the base array, then sort that pointer array. This has especially significant benefits when the data being sorted is large (perhaps kilobytes, or even larger, per item) The original items remain in their original locations in the base array, but now you have a very efficient mechanism in which you can sort them without having to actually move them. You sort the array of pointers to items; the items don't get moved at all.

I realize this is an awful lot to take in, but pointer usage is critical to understanding the many powerful things you can do with the C language, so hit the books and keep refreshing your memory. It will come back.

2 of 4
5

It may be better to declare an actual array, as others have suggested, but your question seems to be more about memory management so I'll discuss that.

struct Test **array1;

This is a pointer to the address of a struct Test. (Not a pointer to the struct itself; it's a pointer to a memory location that holds the address of the struct.) The declaration allocates memory for the pointer, but not for the items it points to. Since an array can be accessed via pointers, you can work with *array1 as a pointer to an array whose elements are of type struct Test. But there is not yet an actual array for it to point to.

array1 = malloc(MAX * sizeof(struct Test *));

This allocates memory to hold MAX pointers to items of type struct Test. Again, it does not allocate memory for the structs themselves; only for a list of pointers. But now you can treat array as a pointer to an allocated array of pointers.

In order to use array1, you need to create the actual structs. You can do this by simply declaring each struct with

struct Test testStruct0;  // Declare a struct.
struct Test testStruct1;
array1[0] = &testStruct0;  // Point to the struct.
array1[1] = &testStruct1;

You can also allocate the structs on the heap:

for (int i=0; i<MAX; ++i) {
  array1[i] = malloc(sizeof(struct Test));
}

Once you've allocated memory, you can create a new variable that points to the same list of structs:

struct Test **array2 = array1;

You don't need to allocate any additional memory, because array2 points to the same memory you've allocated to array1.


Sometimes you want to have a pointer to a list of pointers, but unless you're doing something fancy, you may be able to use

struct Test *array1 = malloc(MAX * sizeof(struct Test));  // Pointer to MAX structs

This declares the pointer array1, allocated enough memory for MAX structures, and points array1 to that memory. Now you can access the structs like this:

struct Test testStruct0 = array1[0];     // Copies the 0th struct.
struct Test testStruct0a= *array1;       // Copies the 0th struct, as above.
struct Test *ptrStruct0 = array1;        // Points to the 0th struct.

struct Test testStruct1 = array1[1];     // Copies the 1st struct.
struct Test testStruct1a= *(array1 + 1); // Copies the 1st struct, as above.
struct Test *ptrStruct1 = array1 + 1;    // Points to the 1st struct.
struct Test *ptrStruct1 = &array1[1];    // Points to the 1st struct, as above.

So what's the difference? A few things. Clearly, the first method requires you to allocate memory for the pointers, and then allocate additional space for the structs themselves; the second lets you get away with one malloc() call. What does the extra work buy you?

Since the first method gives you an actual array of pointers to Test structs, each pointer can point to any Test struct, anywhere in memory; they needn't be contiguous. Moreover, you can allocate and free the memory for each actual Test struct as necessary, and you can reassign the pointers. So, for example, you can swap two structures by simply exchanging their pointers:

struct Test *tmp = array1[2];  // Save the pointer to one struct.
array1[2] = array1[5];         // Aim the pointer at a different struct.
array1[5] = tmp;               // Aim the other pointer at the original struct.

On the other hand, the second method allocates a single contiguous block of memory for all of the Test structs and partitions it into MAX items. And each element in the array resides at a fixed position; the only way to swap two structures is to copy them.

Pointers are one of the most useful constructs in C, but they can also be among the most difficult to understand. If you plan to continue using C, it'll probably be a worthwhile investment to spend some time playing with pointers, arrays, and a debugger until you're comfortable with them.

Good luck!

๐ŸŒ
TutorialsPoint
tutorialspoint.com โ€บ cprogramming โ€บ c_array_of_pointers.htm
Array of Pointers in C
Just like an integer array holds a collection of integer variables, an array of pointers would hold variables of pointer type. It means each variable in an array of pointers is a pointer that points to another address.
๐ŸŒ
Cornell Computer Science
cs.cornell.edu โ€บ courses โ€บ cs3410 โ€บ 2024fa โ€บ notes โ€บ pointer.html
Arrays & Pointers - CS 3410
Remember that ints are 4 bytes on our platform, so these addresses mean that the elements are packed densely, each one next to the other. You can think of the array having a base address \(b\). Then, the address of an element at index \(i\) has this address: ... In fact, C lets you treat an array itself as if it were a pointer to the first element: i.e., the base address \(b\).
๐ŸŒ
OverIQ
overiq.com โ€บ c-programming-101 โ€บ array-of-pointers-in-c
Array of Pointers in C - C Programming Tutorial - OverIQ.com
Just like we can declare an array of int, float or char etc, we can also declare an array of pointers, here is the syntax to do the same. Syntax: dโ€ฆ
๐ŸŒ
Engineering LibreTexts
eng.libretexts.org โ€บ campus bookshelves โ€บ delta college โ€บ c++ programming i (mcclanahan) โ€บ 12: pointers โ€บ 12.4: arrays, pointers and such
12.4.1: Pointer to an Array - Array Pointer - Engineering LibreTexts
May 4, 2025 - When we increment the variable ... 0x7fff4f32fd64 ยท p: is pointer to 0th element of the array arr, while ptr is a pointer that points to the whole array arr....
Top answer
1 of 6
4

Pointer to an array

int a[10];
int (*ptr)[10];

Here ptr is an pointer to an array of 10 integers.

ptr = &a;

Now ptr is pointing to array of 10 integers.

You need to parenthesis ptr in order to access elements of array as (*ptr)[i] cosider following example:

Sample code

#include<stdio.h>
int main(){
  int b[2] = {1, 2}; 
  int  i;
  int (*c)[2] = &b;
  for(i = 0; i < 2; i++){
     printf(" b[%d] = (*c)[%d] = %d\n", i, i, (*c)[i]);
  }
  return 1;
}

Output:

 b[0] = (*c)[0] = 1
 b[1] = (*c)[1] = 2

Array of pointers

int *ptr[10];

Here ptr[0],ptr[1]....ptr[9] are pointers and can be used to store address of a variable.

Example:

main()
{
   int a=10,b=20,c=30,d=40;
   int *ptr[4];
   ptr[0] = &a;
   ptr[1] = &b;
   ptr[2] = &c;
   ptr[3] = &d;
   printf("a = %d, b = %d, c = %d, d = %d\n",*ptr[0],*ptr[1],*ptr[2],*ptr[3]);
}

Output: a = 10, b = 20, c = 30, d = 40

2 of 6
3

Background

Think of pointers as just a separate data type. They have their own storage requirements -- such as their size -- they occupy 8 bytes on a x86_64 platform. This is the case of void pointers void*.

In those 8 bytes the information stored is the memory address of another piece of data.

The thing about pointers is that since they "point" to another piece of data, it's useful to know what type that data is too so you can correctly handle it (know its size, and structure).

In stead of having their own data type name such as pointer they compose their name based on the data type they refer to such as int* a pointer to an integer. If you want a plain pointer without type information attached to it you have the option of using void*.

So basically each pointer (to int, to char, to double) is just a void* (same size, same use) but the compiler knows the data being pointed to is of type int and allows you to handle it accordingly.

/**
 *  Create a new pointer to an unknown type.
 */
void* data;

/**
 *  Allocate some memory for it using malloc
 *  and tell your pointer to point to this new
 *  memory address (because malloc returns void*).
 *  I've allocated 8 bytes (char is one byte).
 */
data = malloc(sizeof(char)*8);

/**
 *  Use the pointer as a double by casting it
 *  and passing it to functions.
 */
double* p = (double* )data;
p = 20.5;
pow((double* )data, 2);

Pointer to array

If you have an array of values (let's say integers) somewhere in memory, a pointer to it is one variable containing its address.

You can access this array of values by first dereferencing the pointer and then operating some work on the array and its values.

/**
 *  Create an array containing integers.
 */
int array[30];
array[0] = 0;
array[1] = 1;
...
array[29] = 29;

/**
 *  Create a pointer to an array.
 */
int (*pointer)[30];

/**
 *  Tell the pointer where the data is.
 */
pointer = &array;

/**
 *  Access the data through the pointer.
 */
(*pointer)[1] = 999;

/**
 *  Print the data through the array.
 *  ...and notice the output.
 */
printf("%d", array[1]);

Array of pointers

If you have an array of pointers to values, the entire array of pointers is one variable and each pointer in the array refers to somewhere else in the memory where a value is located.

You can access this array and the pointers inside it without dereferencing it but in order to reach a certain value from it you will have to dereference one of the pointers inside the array.

/**
 *  Create an array containing pointers to integers.
 */
int *array_of_pointers[30];
array_of_pointers[0] = 0;
array_of_pointers[1] = 1;
...
array_of_pointers[29] = 29;
๐ŸŒ
Litux
litux.nl โ€บ mirror โ€บ cinanutshell โ€บ 0596006977 โ€บ cinanut-CHP-9-SECT-4.html
Section 9.4. Pointers to Arrays and Arrays of Pointers
Pointers occur in many C programs as references to arrays , and also as elements of arrays. A pointer to an array type is called an array pointer for short, and an array whose elements are pointers is called a pointer array ยท For the sake of example, the following description deals with an ...
๐ŸŒ
Lenovo
lenovo.com โ€บ home
Array of Pointers Explained: The Ultimate Guide | Lenovo US
In C/C++, *arr[2] would get you the value pointed to by the third pointer in the array arr. Yes, you can have an array of pointers to arrays. In this setup, each pointer in the array points to the first element of another array.
๐ŸŒ
Computer Hope
computerhope.com โ€บ jargon โ€บ a โ€บ array-of-pointers.htm
What Is an Array of Pointers?
#include <stdio.h> const int ARRAY_SIZE = 5; int main () { /* first, declare and set an array of five integers: */ int array_of_integers[] = {5, 10, 20, 40, 80}; /* next, declare an array of five pointers-to-integers: */ int i, *array_of_pointers[ARRAY_SIZE]; for ( i = 0; i < ARRAY_SIZE; i++) { /* for indices 1 through 5, set a pointer to point to a corresponding integer: */ array_of_pointers[i] = &array_of_integers[i]; } for ( i = 0; i < ARRAY_SIZE; i++) { /* print the values of the integers pointed to by the pointers: */ printf("array_of_integers[%d] = %d\n", i, *array_of_pointers[i] ); } return 0; }
๐ŸŒ
Flylib
flylib.com โ€บ books โ€บ en โ€บ 1.474.1.19 โ€บ 1
An Array of Pointers to Pointers | Data Structures Demystified (Demystified)
The supercharger of pointers is an array of pointers to pointers because an array of pointers to pointers enables you to reorganize tons of data in memory by simply referring to memory addresses. You were introduced to arrays of pointers to pointers at the beginning of this chapter.