What is the type of a pointer to a 2D array?

谁都会走 提交于 2021-02-17 15:15:33

问题


I know that the following is not correct:

int arr[2][3] = {}; //some array initialization here
int** ptr;
ptr = arr;

But I am quite surprised that the following lines actually work

int arr[2][3] = {}; //some array initialization here
auto ptr = arr;
int another_arr[2][3] = {}; //some array initialization here
ptr = another_arr;

Can anyone possibly explain what is the type assigned to ptr in the second block of code, and what happened underneath?


回答1:


Well, arrays decay to pointers when used practically everywhere. So naturally there's decay going on in your code snippet too.

But it's only the "outer-most" array dimension that decays to a pointer. Since arrays are row-major, you end up with int (*)[3] as the pointer type, which is a pointer to a one-dimensional array, not a two dimensional array. It points to the first "row".

If you want ptr's deduction to be a pointer to the array instead, then use the address-of operator:

auto ptr = &arr;

Now ptr is int(*)[2][3].




回答2:


In

auto ptr = arr;

arr decays into a pointer to its first element in the normal way; it's equivalent to

auto ptr = &arr[0];

Since arr[0] is an array of three ints, that makes ptr a int (*)[3] - a pointer to int[3].

another_arr decays in exactly the same way, so in

ptr = another_arr;

both sides of the assignment have the type int (*)[3], and you can assign a T* to a T* for any type T.

A pointer to arr itself has type int(*)[2][3].

If you want a pointer to the array rather than a pointer to the array's first element, you need to use &:

auto ptr = &arr; 



回答3:


First, let's look at why you can't assign int arr[2][3] to int **. To make it easier to visualise, we'll initialise your array with a sequence, and consider what it looks like in memory:

int arr[2][3] = {{1,2,3},{4,5,6}};

In memory, the array data is stored as a single block, just like a regular, 1D array:

arr: [ 1, 2, 3, 4, 5, 6 ]

The variable arr contains the address of the start of this block, and from its type (int[2][3]) the compiler knows to interpret an index like arr[1][0] as meaning "take the value that is at position (1*2 + 0) in the array".

However for a pointer-to-pointer (int**), it is expected that the pointer-to-pointer contains either a single memory address or an array of memory addresses, and this/these adress(es) point to (an)other single int value or array of ints. Let's say we copied the array arr into int **ptrptr. In memory, it would look like this:

ptrptr:     [0x203F0B20, 0x203F17D4]
0x203F0B20: [ 1, 2, 3 ]
0x203F17D4: [ 4, 5, 6 ]

So in addition to the actual int data, an extra pointer must be stored for each row of the array. Rather than converting the two indexes into a single array lookup, access must be performed by making a first array lookup ("take the second value in ptrptr to get an int*"), then nother array lookup ("take the first value in the array at the address held by the previously obtained int*").

Here's a program that illustrates this:

#include <iostream>

int main()
{
    int arr[2][3] = {{1,2,3},{4,5,6}};

    std::cout << "Memory addresses for int arr[2][3]:" << std::endl;
    for (int i=0; i<2; i++)
    {
        for (int j=0; j<3; j++)
        {
            std::cout << reinterpret_cast<void*>(&arr[i][j]) << ": " << arr[i][j] << std::endl;
        }
    }

    std::cout << std::endl << "Memory addresses for int **ptrptr:" << std::endl;
    int **ptrptr = new int*[2];
    for (int i=0; i<2; i++)
    {
        ptrptr[i] = new int[3];
        for (int j=0; j<3; j++)
        {
            ptrptr[i][j] = arr[i][j];
            std::cout << reinterpret_cast<void*>(&ptrptr[i][j]) << ": " << ptrptr[i][j] << std::endl;
        }
    }

    // Cleanup
    for (int i=0; i<2; i++)
    {
        delete[] ptrptr[i];
        ptrptr[i] = nullptr;
    }
    delete[] ptrptr;
    ptrptr = nullptr;

    return 0;
}

Output:

Memory addresses for int arr[2][3]:
0x7ecd3ccc0260: 1
0x7ecd3ccc0264: 2
0x7ecd3ccc0268: 3
0x7ecd3ccc026c: 4
0x7ecd3ccc0270: 5
0x7ecd3ccc0274: 6

Memory addresses for int **ptrptr:
0x38a1a70: 1
0x38a1a74: 2
0x38a1a78: 3
0x38a1a90: 4
0x38a1a94: 5
0x38a1a98: 6

Notice how the memory addresses always increase by 4 bytes for arr, but for ptrptr there is a jump of 24 bytes between values 3 and 4.

A simple assignment can't create the pointer-to-pointer structure needed for type int **, which is why the loops were necessary in the above program. The best it can do is to decay the int[2][3] type into a pointer to a row of that array, i.e. int (*)[3]. That's what your auto ptr = arr; ends up as.




回答4:


What is the type of [...]

Did you already try to ask the compiler to tell you the type of an expression?

int main()
{
    int arr[2][3] = {{0,1,2}, {3,4,5}};  // <-- direct complete initialized here

    auto ptr = arr;                     // <-- address assignment only

    cout << "arr: " << typeid(arr).name() << endl;
    cout << "ptr: " << typeid(ptr).name() << endl;
    return 0;
}

I've to confess that the output

arr: A2_A3_i
ptr: PA3_i

seems to be not very readable at first glance (compared to some other languages), but when in doubt it may help. It's very compact, but one may get used to it soon. The encoding is compiler-dependent, in case you are using gcc, you may read Chapter 29. Demangling to understand how.

Edit:

some experimentation with some simple_cpp_name function like this rudimentary hack

#include <typeinfo>
#include <cxxabi.h>
#include <stdlib.h>
#include <string>

std::string simple_cpp_name(const std::type_info& ti)
{
    /// simplified code extracted from "Chapter 29. Demangling"
    /// https://gcc.gnu.org/onlinedocs/libstdc++/manual/ext_demangling.html
    char* realname = abi::__cxa_demangle(ti.name(), 0, 0, 0);
    std::string name = realname;
    free(realname);
    return name;
}

will show you that auto &rfa = arr; makes rfa having the same type as arr which is int [2][3].



来源:https://stackoverflow.com/questions/45409737/what-is-the-type-of-a-pointer-to-a-2d-array

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!