6

C++11 code:

int a[3];
auto b = a;       // b is of type int*
auto c = &a;      // c is of type int(*)[1]

C code:

int a[3];
int *b = a;
int (*c)[3] = &a;

The values of b and c are the same.

What is the difference between b and c? Why are they not the same type?

UPDATE: I changed the array size from 1 to 3.

Domi
  • 22,151
  • 15
  • 92
  • 122

3 Answers3

7

The sizeof operator should behave differently, for one, especially if you change the declaration of a to a different number of integers, such as int a[7]:

int main()
{
    int a[7];

    auto b = a;
    auto c = &a;

    std::cout << sizeof(*b) << std::endl;  // outputs sizeof(int)
    std::cout << sizeof(*c) << std::endl;  // outputs sizeof(int[7])

    return 0;
}

For me, this prints:

4
28

That's because the two pointers are very different types. One is a pointer to integer, and the other is a pointer to an array of 7 integers.

The second one really does have pointer-to-array type. If you dereference it, sure, it'll decay to a pointer in most cases, but it's not actually a pointer to pointer to int. The first one is pointer-to-int because the decay happened at the assignment.

Other places it would show up is if you really did have two variables of pointer-to-array type, and tried to assign one to the other:

int main()
{
    int a[7];
    int b[9];

    auto aa = &a;
    auto bb = &b;

    aa = bb;

    return 0;
}

This earns me the error message:

xx.cpp: In function ‘int main()’:
xx.cpp:14:8: error: cannot convert ‘int (*)[9]’ to ‘int (*)[7]’ in assignment
     aa = bb;

This example, however, works, because dereferencing bb allows it to decay to pointer-to-int:

int main()
{
    int a;
    int b[9];

    auto aa = &a;
    auto bb = &b;

    aa = *bb;

    return 0;
}

Note that the decay doesn't happen on the left side of an assignment. This doesn't work:

int main()
{
    int a[7];
    int b[9];

    auto aa = &a;
    auto bb = &b;

    *aa = *bb;

    return 0;
}

It earns you this:

xx2.cpp: In function ‘int main()’:
xx2.cpp:14:9: error: incompatible types in assignment of ‘int [9]’ to ‘int [7]’
     *aa = *bb;
Community
  • 1
  • 1
Joe Z
  • 17,413
  • 3
  • 28
  • 39
  • `sizeof` also discards the `const` part for your second `cout` with the `c` variable. That's why they result in identical output. – user2485710 Dec 08 '13 at 14:55
  • @user2485710 : What results in identical output? My two `cout` lines output two different numbers. `const` doesn't come into the picture at all. I'm not able to align your comment with anything in my post. – Joe Z Dec 08 '13 at 14:57
  • Great answer. Highlights a whole bunch of concepts that I have not given much thought to, previously! I probably should use array types in favor of pointer types more often! – Domi Dec 08 '13 at 15:36
  • @KerrekSB : I first learned C over 20 years ago. That part is baked in my fingers, similar to the convulsions I get when I see `void main(void)`. And since I still need to program C, at least that part isn't actively harmful. Also, if you use a library that wraps `main` with something else through a `#define` (I'm looking at you, SDL), better safe than sorry... – Joe Z Dec 08 '13 at 15:52
6

The identity of any object in C++ is determined by the pair of its type and its address.

There are two distinct objects with the same address in your example: The array itself, and the first element of the array. The first has type int[1], the second has type int. Two distinct objects can have the same address if one is a subobject of the other, as is the case for array elements, class members, and class base subobjects.

Your example would be clearer if you wrote:

int a[5];
int (*ptr_to_array)[5] = &a;
int * ptr_to_array_element = &a[0];

But you have taken advantage of the fact that the id-expression a for the array decays to a pointer to the array's first element, so a has the same value as &a[0] in your context.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
1

Consider this example:

#include<stdio.h>

int main()
{
    int myArray[10][10][10][10]; //A 4 Dimentional array;

    //THESE WILL ALL PRINT THE SAME VALUE
    printf("%d, %d, %d, %d, %d\n",
            myArray,
            myArray[0],
            myArray[0][0],
            myArray[0][0][0],
            &myArray[0][0][0][0]
          );

    //NOW SEE WHAT VALUES YOU GET AFTER ADDING 1 TO EACH OF THESE POINTERS
    printf("%d, %d, %d, %d, %d\n",
            myArray+1,
            myArray[0]+1,
            myArray[0][0]+1,
            myArray[0][0][0]+1,
            &myArray[0][0][0][0]+1
          );
}

You will find that all the 5 values printed in first case are all equal. Because they point to the same initial location.

But just when you increment them by 1 you see that different pointers now jump (point) to different locations. This is because myArray[0][0][0] + 1 will jump by 10 integer values that is 40 bytes, while myArray[0][0] + 1 will jump by 100 integer values i.e by 400 bytes. Similarly myArray[0] + 1 jumps by 1000 integer values or 4000 bytes.

So the values depend on what level of pointer you are referring to.

But now, if I use pointers to refer all of them:

#include<stdio.h>

int main()
{
    int myArray[10][10][10][10]; //A 4 Dimentional array;

            int * ptr1 = myArray[10][10][10];
            int ** ptr2 = myArray[10][10];
            int *** ptr3 = myArray[10];
            int **** ptr4 = myArray;

    //THESE WILL ALL PRINT THE SAME VALUE
    printf("%u, %u, %u, %u\n", ptr1, ptr2, ptr3, ptr4);

    //THESE ALSO PRINT SAME VALUES!!
    printf("%d, %d, %d, %d\n",ptr1+1,ptr2+1,ptr3+1,ptr4+1);
}

So you see, different levels of pointer variables do not behave the way the array variable does.