William M. answered 12/22/19
STEM Tutor (Ph.D, Northwestern) | Algebra–Calc, Bio, Chem, Physics, CS
The main rule is: you have to read from the inside out.
Your first declaration and your third declaration said:
// (1) int* arr1[8];
// (3) int* (arr1[8]);
In case (3), we can throw away the parentheses, because C/C++'s precedence rules make them unnecessary. That means case (3) is the same as case (1).
Ok, then, how to read case (1) ? int* arr1[8];
Read the inside first...
It's an array of 8...
Then look at the data type (int *).
So we have,
int* arr1[8] is an array of 8 ptrs to ints.
What about case (2) ?
int (*arr2)[8];
Read from the inside out:
It's a pointer...
to an array of 8...
ints
int (*arr2)[8] is a pointer to an array of 8 ints.
What is the general rule for understanding more complex declarations?
Let's look at some more examples...
#include <stdio.h>
int main(int argc, const char* argv[]) {
char c = 'A'; // individual char
int x = 5; // individual ints
int y = 10;
int z = 15;
char* pc = &c; // a pointer to a char (say "char star")
int* px = &x; // pointers to ints. (say "int star" -- a ptr to an int)
int* py = &y;
int* pz = &z;
// Why is it better to write it this way?
// Because that's how we decode the more complex declarations
// Note: write pointer declarations on individual lines
int* ps; // good
int* pt; // Now, we always write pointers to int as int* ps.
int *ps, pt; // wrong: pt is an int ! don't declare multiple ptrs on 1 line
int arr_int[] = { x, y, z }; // this is an array of ints
// you wrote it as int* arr1[8]
// this is also equal to: int* (arr1[8]);
// i.e., parentheses could be thrown away
int* arr_pint[3] = { &x, &y, &z }; // an array of int*
int (*p_arrint)[3] = &arr_int; // a ptr to an array of ints
// your example was int (*arr2)[8];
// Let's take it to the next level!
int* (*p_arr_pint)[3] = &arr_pint; // a ptr to an array of ptrs to ints
return 0;
}
Hope that helps...