Because there are just too many pointers.

int i = 100; // int variable
int* ip = &i; // int pointer

Here ip is a pointer that stores on the stack, and points to int variable i. By “points to”, it means the variable stores the address in memory where i stays.

int ar[5]; // int array, store on the stack
ar[0] = 1;
cout << ar << endl;	// here the output is an address
cout << *ar << endl; // dereference it, got the value of ar[0]
ar[1] = 2;
cout << *(ar+1) << endl; // move the pointer to get the value of ar[1]
int* p4 = ar+3; // create a pointer pointing to the 4th element
int* arp = ar;

Here ar is an array of integers, and the array stays on the stack. However, the variable is internally treated as a pointer pointing to the first element of the array. Therefore, when we print it out, the result is the address that stores the first element. To print out the actual value of first element, we could even dereference the variable.

In addition, since fixed-length array is stored sequentially, once we have the pointer, we could reach any element with proper offset. For example, you could create a pointer to point to any element like the second to last line. If you explicitly create a pointer pointing to the array, it behaves the same as the array variable itself.

int* ar_pointer[5];
ar_pointer[0] = ip;

This creates a fix-length array of pointers pointing to int variables. All elements are initialized but unassigned. Elements of array are stored on the stack.

const int* c_ip = &i; // a pointer pointing to a const int variable
int const* ip_c = &i // a const pointer pointing to an int

Two very confusing concept. The first one creates a normal pointer pointing to a const int variable; however, it is necessary that it is pointing to an actual const variable. Basically, you cannot change the value of i using say *c_ip = 2;, because from the pointer’s perspective it is a const variable. But, you could change the variable directly by i = 2;, as i itself is not a const int. The second one creates a const pointer, which effectively means it can only point to the address of memory it has been assigned to when it got created. The value of that piece of memory could be changed.

const int j = 0;
const int* const c_ip = &j;

Yes, by adding const to both places we have a const pointer pointing to a const variable. The pointer cannot point to other address, and the value of j cannot be changed.

// takes a pointer of int as argument
void func(int* i); 
// takes an array of int as argument
void arrayFunc(int a[]); 
// takes an array of pointers of int as argument
void arrayPointerFunc(int* a[]); 
// takes an array of 10 int as argument	
void arrayFunc(int a[10]); 

As array variable itself is a pointer, the second function could also take a pointer as input argument. Also, the array argument will always be pass-by-pointer, that is instead of passing only values, the argument passes a pointer, whose modification will directly apply to the original array.

One more thing to note, if function states the length of the input, and we give it a shorter array, it will write data to memory that is beyond the given array; on the other hand, if we give it a longer array, it will only use the stated length of that array.

reference

This does not mean the reference where we put all citations, which you could find at the bottom. Reference is an alias of a variable. You are modifying the variable when you modifying its reference. A const reference to some variable means you could access, but not modify the variable.

const int &refint = i;
int &refval = classA.val.next.classB.val;

One common way to use reference is to create const reference to prevent methods who use the variable from modifying the variable. Also, alias could reduce overhead when the variable you want to access has a long chain.

Reference

[1] learncpp.com