Search code examples
c++arrayspointersreferencedereference

Looking for some confirmations on pointer usage differences


I'm currently in programming 2 at my college. We have a test coming up and just wanted to ask for some confirmation on a few things I put together on pointers. Consider the following code:

//CASE 1

int Awesome = 10;        //Line 1
int *P = &Awesome;       //Line 2

cout << Awesome << endl; //Line 3
cout << *P << endl;      //Line 4

//CASE 2

int X[3]={0,0,5};        //Line 1
int *A = X;              //Line 2

cout << X[2] << endl;    //Line 3
cout << A[2] << endl;    //Line 4

Here's where my questions come in, but I think I sort of "stumbled" onto the answers of my questions, but I want to make sure its correct.

In the first line 2, I declare pointer P and give it Awesome's address by using the reference operator. In the second line 2, I declare pointer A and give it the address of X. I never understood why we didn't use the & operator in the case of giving an array address to a pointer. After thinking about it, I recall hearing that the array name is just a constant pointer containing the address the first subscript of the array. If thats correct, then it would make sense why we wouldn't need to use the & to obtain the address of the array, because the array name itself already contains the address. Is this correct?

My second question. In the first line 4, I'm cout-ing the contents of what pointer P is pointing at by using the dereference operator, but in the second 4th line, I don't need to use the * operator to obtain the value. In my head, I wasn't sure why it differed, but I think the concept in the paragraph above made it more clear. The array name itself and a pointer are basically the same thing (except the array name is a constant pointer I think). An array name is a constant pointer that holds the address and the pointer is getting this address. Therefore, we use the new pointer holding the new address the same way. Its not the same as the first instance at all. Since an array name and a pointer are basically the same, we don't need to use the special * operator, just use it like a normal array with array notation. Is this correct?

I've been struggling to understand why the two concepts above differed, why in one instance (int *P = &Address) we needed to use the & operator to give an address and why in another instance (int *A=X) we didn't. And I was struggling with why we use the * in one instance (cout << *P;) and not in another (cout << A[2];). The differences had me very confused, but if I'm anywhere close to being correct with my logic, it makes total sense now. Can anyone confirm this or correct it if its entirely wrong?


Solution

  • It is not formally correct that arrays and pointers are the same type.

    Here's proof, using an overloaded function. The first overload takes an array by reference, the second takes a pointer by reference. It shows that int[8] and int* are distinct types.

    #include <iostream>
    
    void f(int (&x) [8]) {
        std::cout << "array type\n";
    }
    
    void f(int*& y) {
        std::cout << "pointer type\n";
    }
    
    int main()
    {
        int x[8];
        int* y;
    
        f(x);
        f(y);
    }
    

    What's true is that arrays can easily be converted to a pointer to their first element. This often happens when beginners try to pass an array by value to a function. There must be a question on why this does not work every 2 or 3 days here at Stackoverflow. This conversion means that, for example, functions void f(int x[8]) and void f(int* x) would be identical to each other (and the compiler would complain about a redefinition). Above all, this means of course that the size information ([8]), which is part of the array type, is lost.

    The conversion is informally called "decay", and it also happens at assignment or initialisation, like here:

    int X[3]={0,0,5};        //Line 1
    int *A = X;              //Line 2
    

    This does not mean that int[3] and int* are identical, much like int and double are not identical just because you can write my_double = my_int;.

    If thats correct, then it would make sense why we wouldn't need to use the & to obtain the address of the array, because the array name itself already contains the address. Is this correct?

    No. You do need to use & to obtain the address of the array. You do not need it to obtain the value contained in its first element.

    In your example:

    int X[3]={0,0,5};        //Line 1
    int *A = X;              //Line 2
    

    std::cout << &X; would give you the address of the local array object.

    Since an array name and a pointer are basically the same, we don't need to use the special * operator, just use it like a normal array with array notation. Is this correct?

    Well, it depends on what you really mean with the weasel word "basically".

    After all, the subscript operator is just syntactical sugar for pointer arithmetics [*]. t[n] is by definition equal to *(t + n) (which is, ironically, identical to *(n + t), which is consequently identical to n[t]).

    int X[3]={0,0,5};        //Line 1
    int *A = X;              //Line 2
    
    cout << X[2] << endl;    //Line 3
    cout << A[2] << endl;    //Line 4
    

    In line 3, you do *(X + 2). That's fine. X "decays" to a pointer to its first element, and then 2 is added to that pointer. The result of the pointer addition is dereferenced.

    In Line 4, you do *(A + 2). That's also fine. The only difference is that no "decay" takes place; A is already of a pointer type.


    By the way... In your question and comments, you keep mentioning that an "array name itself is just holding an address". This means less than you seem to think it does. After all, it just states the very simple fact that the name of a variable denotes an object which lives in memory and therefore must have an address. You could say that about a simple int variable. For example, in int x = 123;, one could say that "the int name itself is just holding an address". This is somewhat true, but I don't think it's a useful piece of the puzzle for someone trying to understand arrays and pointers.


    [*] Which proves of course that syntactical sugar can be very important to write maintainable code! :)