R.X*_*Xin 21 c++ arrays pointers multidimensional-array c++11
我知道以下内容不正确:
int arr[2][3] = {}; //some array initialization here
int** ptr;
ptr = arr;
Run Code Online (Sandbox Code Playgroud)
但我很惊讶以下几行确实有效
int arr[2][3] = {}; //some array initialization here
auto ptr = arr;
int another_arr[2][3] = {}; //some array initialization here
ptr = another_arr;
Run Code Online (Sandbox Code Playgroud)
任何人都可以解释在第二个代码块中分配给ptr的类型是什么,以及下面发生了什么?
Sto*_*ica 26
好吧,当几乎无处不在时,阵列会衰减到指针.所以你的代码片段自然也会出现衰退.
但它只是"最外层"的数组维度衰减到指针.由于数组是行主要的,你最终会int (*)[3]得到指针类型,它是指向一维数组的指针,而不是二维数组.它指向第一个"行".
如果你想要ptr推断是一个指向数组的指针,那么使用address-of运算符:
auto ptr = &arr;
Run Code Online (Sandbox Code Playgroud)
现在ptr是int(*)[2][3].
mol*_*ilo 12
在
auto ptr = arr;
Run Code Online (Sandbox Code Playgroud)
arr以正常方式衰减到指向其第一个元素的指针; 它相当于
auto ptr = &arr[0];
Run Code Online (Sandbox Code Playgroud)
因为arr[0]是一个三个ints 的数组,所以它是ptr一个int (*)[3]- 指针int[3].
another_arr 以完全相同的方式衰变,所以
ptr = another_arr;
Run Code Online (Sandbox Code Playgroud)
转让双方有型int (*)[3],你可以分配T*到T*任何类型T.
指向arr自身的指针有类型int(*)[2][3].
如果你想要一个指向数组的指针而不是指向数组第一个元素的指针,你需要使用&:
auto ptr = &arr;
Run Code Online (Sandbox Code Playgroud)
首先,让我们来看看为什么你不能分配int arr[2][3]给int **.为了更容易可视化,我们将使用序列初始化您的数组,并考虑它在内存中的样子:
int arr[2][3] = {{1,2,3},{4,5,6}};
Run Code Online (Sandbox Code Playgroud)
在内存中,数组数据存储为单个块,就像常规的1D数组一样:
arr: [ 1, 2, 3, 4, 5, 6 ]
Run Code Online (Sandbox Code Playgroud)
该变量arr包含该块的起始地址,并且从其类型(int[2][3])开始,编译器知道将索引解释arr[1][0]为意味着"获取数组中位置(1*2 + 0)的值".
但是对于指向指针(int**)的指针,预期指向指针的指针包含单个内存地址或内存地址数组,而这个/这些地址指向(一个)其他单个int整数的值或数组.假设我们将数组复制arr到了int **ptrptr.在内存中,它看起来像这样:
ptrptr: [0x203F0B20, 0x203F17D4]
0x203F0B20: [ 1, 2, 3 ]
0x203F17D4: [ 4, 5, 6 ]
Run Code Online (Sandbox Code Playgroud)
因此,除了实际int数据之外,还必须为数组的每一行存储额外的指针.不是将两个索引转换为单个数组查找,而是必须通过进行第一次数组查找("在ptrptr中获取第二个值以获取int*")来执行访问,然后进行数组查找("获取第一个值数组在先前获得的int*")所持有的地址处.
这是一个程序,说明了这一点:
#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;
}
Run Code Online (Sandbox Code Playgroud)
输出:
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
Run Code Online (Sandbox Code Playgroud)
注意内存地址总是增加4个字节arr,但是ptrptr在值3和4之间有24个字节的跳转.
简单赋值不能创建类型所需的指针到指针结构int **,这就是上述程序中循环所必需的原因.它能做的最好的事情是将int[2][3]类型衰减为指向该数组行的指针,即int (*)[3].这就是你auto ptr = arr;最终的结果.