4
int q[10]={0};
cout << q << endl;
cout << &q << endl;
cout << &q[0] << endl;

output is

0x7fffd4d2f860 
0x7fffd4d2f860 
0x7fffd4d2f860 

Now when i do this->

int *r=q;    // allowed
int *r=&q[0] // allowed
int *r=&q    // not allowed

Why is the third assignment not allowed when it is essentially the same thing?

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • 5
    `&q` would be `int **` – ALX23z Mar 20 '20 at 22:15
  • Then why does cout print same things in all of the three cases? –  Mar 20 '20 at 22:16
  • 3
    @jamesgem Because there is an `operator<<(void*)` defined for `std::ostream`, and `void*` can accept ANY type of pointer (well, almost any). All three of your `<<` calls resolve to the same memory address, which is why they all print the same number. – Remy Lebeau Mar 20 '20 at 22:18
  • 1
    @jamesgem You seem to be confusing **type** and **value**. `cout` prints the value. `cout` will not tell you if an expression is a valid type for assignment. – Drew Dormann Mar 20 '20 at 22:18
  • 2
    My compiler spits out the following error:`error: cannot convert 'int (*)[10]' to 'int*' in initialization` From that it's pretty easy to see that the type is wrong.Good compiler messages are golden. – user4581301 Mar 20 '20 at 22:20
  • Just because the printed values are the same doesn't mean the types of the expressions are the same. – eesiraed Mar 20 '20 at 22:27
  • 3
    @Timo Not only is `&q` not `int**`, but also it does not decay to it. Pointers to arrays do not decay. – eerorika Mar 20 '20 at 22:44
  • 1
    @Timo [`&q` does not decay into an `int**`](https://ideone.com/523n42) – Remy Lebeau Mar 20 '20 at 22:44
  • To answer the question in the title literally, you **can** assign the address of an array to a pointer, but the pointer has to have the right type. – Pete Becker Mar 21 '20 at 14:18

5 Answers5

9

If you have an array declared like

T a[N];

where T is some type specifier then a pointer to the array will be declared like

T ( *p )[N] = &a;

A general rule is the following. If you have a multidimensional array (including one-dimensional arrays) like for example

T a[N1][N2][N3];

then this declaration you may rewrite like

T ( a[N1] )[N2][N3];

To get a pointer to the first element of the array just substitute the content in the parentheses the following way

T ( *p )[N2][N3] = a;

If you want to get a pointer to the whole array then rewrite the declaration of the array like

T ( a )[N1][N2][N3];

and make the substitution

T ( *p )[N1][N2][N3] = &a;

Compare this with a declaration of a scalar object and a pointer to it.

For example

T obj;

You may rewrite the declaration like

T ( obj );

Now to get a pointer to the object you can write

T ( *p ) = &obj;

Of course in this case the parentheses are redundant and the above declaration is equivalent to

T *p = &obj;

As for this code snippet

int q[10]={0};
cout << q << endl;
cout << &q << endl;
cout << &q[0] << endl;

and its output

0x7fffd4d2f860 
0x7fffd4d2f860 
0x7fffd4d2f860 

then array designators used in expressions with rare exceptions are converted to pointers to their first elements.

So in fact the two expression q and &q[0] in these statements

cout << q << endl;
cout << &q[0] << endl;

are equivalent. On the other hand, the address of the array itself is the address of the memory extent that the array occupies. And in the beginning of the extent there is the first element of the array. So the three expressions give the same result: the address of the extent of memory occupied by the array.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • 1
    This would make a nice FAQ answer imo. – Timo Mar 20 '20 at 22:33
  • Change the cout's to something like `cout << q << " " << typeid(q).name() << endl;`, that'll provide some insight (I hope). Need a `#include ` too. – Eljay Mar 20 '20 at 23:27
  • @Eljay `typeid(something).name()` does not provide human-understandable output in most cases. I implemented your suggestion in [this answer](https://stackoverflow.com/a/75272572/8659747). – Kitswas Jan 29 '23 at 04:30
3

Why is the third assignment not allowed when it is essentially the same thing?

Because The C++ language has a feature called "type safety". There is a type system that helps you keep the logic of your program sound.

One particular rule is that arbitrary pointer types can not be used to initialise pointers of other, incompatible types. In this case, you have a pointer to type int (.i.e. int*) that you try to initalise with an expression of type pointer to type array of 10 int (i.e. int(*)[10]). One type is not implicitly convertible to the other, hence the program is ill-formed.

Then why does cout print same things in all of the three cases?

Because all of the pointers have the same value. The first byte of the first element of the array is the same byte as the first byte of the entire array.

It just so happens that the stream insertion operator handles all pointer types1 exactly the same, so pointers with same value but different type produce the same output.

1 Pointers to character types are an exception. They are treated entirely differently.


Why can't we assign address of array to pointer?

Actually, we can assign address of an array to a pointer. We just cannot assign address of a array (or any other object for that matter) to a pointer of wrong type. We need a pointer to an array in this case:

int (*r)[10] = &q;
eerorika
  • 232,697
  • 12
  • 197
  • 326
1

You cannot do the third assignment because &q's type is an int (*)[10], which is incompatible with the type of int* r.

The output of cout << &q does not reveal the type of &q. See this documentation link.

Kurt Stolle
  • 322
  • 4
  • 18
1

q is a fixed-length array. Specifying q by itself in an expression decays into a pointer to the 1st element of q. Thus, q decays to the same pointer value that &q[0] returns. &q, on the other hand, returns the memory address of the q variable itself, and for an array, its 1st element occupies that same memory address.

There is an operator<<(void*) defined for std::ostream, and void* can accept (almost) ANY type of pointer. Since all three of your cout calls resolve to the same memory address, and there is an operator<< that accepts all three types of pointers, that is why all three calls print the same number.

As for your assignments:

  • q is an int[10], which decays into an int*, which is why int *r=q; works.

  • &q[0] dereferences q to access its 1st element, which is an int, and then takes the address of that element, producing an int*, which is why int *r=&q[0]; works.

  • since q is an int[10], &q is an int(*)[10], which DOES NOT decay into an int*, which is why int *r=&q; does not work. You would have to declare r using the correct type:

    int (*r)[10] = &q;

Community
  • 1
  • 1
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
0

The following code demonstrates the differences between arr, &arr and &arr[0] where arr is an array of integers.

#include <iostream>
#include <string_view>

// Reference: https://stackoverflow.com/questions/81870/is-it-possible-to-print-a-variables-type-in-standard-c/56766138#56766138
// Type finding code start
template <typename T>
constexpr auto type_name()
{
    std::string_view name, prefix, suffix;
#ifdef __clang__
    name = __PRETTY_FUNCTION__;
    prefix = "auto type_name() [T = ";
    suffix = "]";
#elif defined(__GNUC__)
    name = __PRETTY_FUNCTION__;
    prefix = "constexpr auto type_name() [with T = ";
    suffix = "]";
#elif defined(_MSC_VER)
    name = __FUNCSIG__;
    prefix = "auto __cdecl type_name<";
    suffix = ">(void)";
#endif
    name.remove_prefix(prefix.size());
    name.remove_suffix(suffix.size());
    return name;
}
// Type finding code end

int main()
{
    int arr[5] = {1, 2, 3, 4, 5};
    std::cout << "Value: " << arr << "\tType: " << type_name<decltype(arr)>() << std::endl;
    std::cout << "Value: " << &arr << "\tType: " << type_name<decltype(&arr)>() << std::endl;
    std::cout << "Value: " << &arr[0] << "\tType: " << type_name<decltype(&arr[0])>() << std::endl;
    return 0;
}

See it in action.

Sample Output:

Value: 0x7ffcdadd22d0   Type: int [5]
Value: 0x7ffcdadd22d0   Type: int (*)[5]
Value: 0x7ffcdadd22d0   Type: int*

While all three are associated with the same memory location, they are of different types.

Reference: Is it possible to print a variable's type in standard C++?

Kitswas
  • 1,134
  • 1
  • 13
  • 30
  • There is also the older Boost [demangle](https://www.boost.org/doc/libs/1_81_0/libs/core/doc/html/core/demangle.html), or the newer Boost [typename](https://www.boost.org/doc/libs/1_81_0/libs/core/doc/html/core/type_name.html). – Eljay Jan 29 '23 at 12:06