简体   繁体   中英

Why is it that, when I dereference an array pointer, the resulting value is a pointer to the first element of the array, not the entire array object?

#include<iostream>

int num[3]={66,77,88};

int main()
{
    int(*pi)[3]=&num;
    std::cout<<*pi;
} 

The result is an address instead of an array. What is the explanation behind this?

not the entire array object?

*pi gives the array, ie the int[3] . But operator<< for std::basic_ostream doesn't have overloads taking array but has an overload taking pointer ( const void* ), then array-to-pointer decay happens, the converted pointer ( int* ) is pointing to the 1st element of the array and then is converted to const void* , then is passed to std::cout and gets printed out.

There is an implicit conversion from lvalues and rvalues of array type to rvalues of pointer type: it constructs a pointer to the first element of an array. This conversion is used whenever arrays appear in context where arrays are not expected, but pointers are:

Just like cout cannot print a vector, it cannot print an array. However cout will print the address of the first element.

If you take a look at the source code of iostream they mention cout as a pointer https://code.woboq.org/llvm/libcxx/src/iostream.cpp.html , and that is also why you always need to iterate over each item of your array as oppose to other languages (like Rust) where you can print a vector or an array.

For string there is an exception since in C and C++ they are (or should be,) NULL terminated. and that is why the whole char array is printed, But 0 is a valid number. hence there is no way to know the bounds of an int array unless you can detect it at compile time (which Rust does).

When an array such as int[3] is printed, it is passed to a function whose signature in principle takes the array as an argument. It could be any of

   ostream & operator <<(ostream &s, int *value);
   ostream & operator <<(ostream &s, int value[]);
   ostream & operator <<(ostream &s, int value[3]);

However, in C/C++ these are all equivalent to the first one. Passing an array as argument to a function just passes a pointer to the beginning of the array. Therefore, the size of the array is not (directly) known to the function.

If we consider a vector instead of an array, then the vector object itself knows its size and we can print the vector. There is no built-in method for this, so we would have to make one ourselves (in this case just for a vector of int to illustrate the idea):

#include <iostream>
#include <vector>

std::ostream &operator << (std::ostream &s, const std::vector<int> &v)
{
    s << "(";
    for(auto &e: v)
    {
        if(&e != &v[0])
        {
            s << ", ";
        }
        s << e;
    }
    s << ")";
    return s;
}

int main()
{
    std::vector<int> num = {66, 77, 88};
    std::cout << num;
}

The result is:

    (66, 77, 88)

It's not something I would normally recommend, but, if you define an override of operator<<( ostream&, const int[3] ) , the compiler will run that, instead of decaying the array pointer to void* or something else.

Here's some sample code that demonstrates how C++ implicitly converts the right-hand side of operator<< .

#include <cstddef>
#include <cstdlib>
#include <iostream>

using std::ostream;
using std::ptrdiff_t;

template <ptrdiff_t N>
  ostream& operator<< ( ostream& out, const int (&a)[N] )
  {
    if constexpr (N > 1) {
      out << a[0];
      out << ", ";
      return ( out << *(const int(*)[N-1])(&(a[1])) );
    } else if constexpr (N == 1) {
      out << a[0];
    }

    return out;
  }

constexpr int nums[] = { 66, 77, 88 };
constexpr char letters[] = { 'a', 'b', 'c', 0 };
constexpr float reals[] = { 1.23f, 4.56f, 7.89f };

int main()
{
    std::cout << nums << '\n';
    static const int (*const pi)[3] = &nums;
    std::cout << *pi << std::endl;

    std::cout << (const char*)letters << '\n';
    std::cout << letters << std::endl;

    std::cout << (const void*)reals << '\n';
    std::cout << reals << std::endl;

    return EXIT_SUCCESS;
}

(You could certainly quibble with the implementation.)

If you provide an overload that exactly matches const int[3] , it calls that. With a const char[4] , the compiler tries decaying to a pointer of the element type, so the best match the compiler finds is const char* . When there is no match for const float[3] or const float* , the array decays to a const float* and implicitly converts to a void* .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM