指针数组 (*A)[] 和双指针 **A 之间的区别

Posted

技术标签:

【中文标题】指针数组 (*A)[] 和双指针 **A 之间的区别【英文标题】:Difference between array of pointers (*A)[] and double pointer **A 【发布时间】:2018-05-18 23:20:30 【问题描述】:

对于一维数组(例如整数),我知道如何为堆和堆栈中的分配创建指针

-栈中的指针和栈中的数组

int a[3];
int *ptr=a;

-堆栈中的指针和堆中的数组

int *ptr=new int[3];

但是对于矩阵(例如 3x2),指针可以创建为指针数组int (*A)[3] 或双指针int **A

我想知道这两种创建指向多维数组的指针的主要区别。

特别是我已经看到了用于创建指向存储在堆栈中的矩阵数组的指针的指针数组:

int A[2][3];
int (*ptr)[3]=A;

我已经看到了用于在堆中分配的矩阵的双指针

int **ptr= new *[3];
for(int i=0; i<=3; i++) int ptr[i]=new [2];

但是是否也可以将指针数组用于堆,将双指针用于堆栈?

再一次,更一般地说,指针数组和双指针之间的主要区别是什么?

【问题讨论】:

这样的问题每周会在 SO 上弹出一次。搜索时发现了什么? This post 详细介绍了 C 中的数组内部结构。存在明显差异(某些技术不能应用于 C++),但内存布局的一般要点是相同的。 对于矩阵,我通常更喜欢使用一维数组(顺便说一句。这就是 int a[2][3] 通常在 AFAIK 下所做的),由于连续内存,性能要好得多(与用int *a[2]单独分配每一行或每一列)。此外,出于同样的原因(dcache 性能),您的矩阵是行优先还是列优先以及随后如何迭代它也很重要。 例如***.com/questions/33746434/… / ***.com/questions/33491687/… / 更多 Difference between double pointer and array of pointers的可能重复 【参考方案1】:

来自 C++ (2017) 标准(7.2 数组到指针的转换)

1 类型为“N T 数组”或“未知数组”的左值或右值 T 的边界”可以转换为“指向 T 的指针”类型的纯右值。这 应用临时实现转换 (7.4)。 结果是 指向数组第一个元素的指针

假设你有一个这样的数组

T A[N1][N2]...[Nn];

其中T 是某种类型,[N1][N2]...[Nn] 是数组维度的非正式记录。那么这个声明也可以这样写

T ( A[N1] )[N2]...[Nn];

要为数组的第一个元素声明一个pojnter,您只需在声明中用( A[N1] ) 替换( *ptr )

T ( A[N1] )[N2]...[Nn];
T ( *ptr  )[N2]...[Nn] = A;

例如从问题中获取声明

int A[2][3];

你可以像这样重写它

int ( A[2] )[3];

现在很容易声明指向数组第一个元素的指针

int ( *ptr )[3] = A;

解引用得到二维数组int[3]类型的第一“行”的指针

另一方面,如果你有一个指针数组的声明,比如

int * A[3];

可以像这样重写

int * ( A[3] );

然后得到一个你可以写的指针的声明

int * ( *ptr ) = A;

与此相同

int **ptr = A;

所以解除对指针的引用你会得到一个int *类型的对象,它又是一个指针。

所以对于这个声明

int ( *ptr )[3] = A;

指向的对象是一个一维数组。比如你可以写

std::cout << sizeof( *ptr ) << std::endl;

你会得到一个等于sizeof( int[3] )的值,等于3 * sizeof( int )

至于这份声明

int * A[3];
int **ptr = A;

指向的对象是int * 类型的指针。如果为这个指针写

std::cout << sizeof( *ptr ) << std::endl;

那么你会得到一个等于sizeof( int * )的值

让我们考虑一下这段代码sn-p

int **ptr = new int *[2];
for( int i = 0; i < 2; i++ ) ptr[i] = new int[3];

在第一条语句中,动态分配了一个int *[2] 类型的一维数组。

然后在循环中动态创建 2 个 int[3] 类型的数组,并将指向数组第一个元素的指针分配给先前分配的一维数组的元素。

所以总体上动态分配了 3 个数组:一个是 int *[2] 类型,另一个是 int [3] 类型。也就是说,分配了三个单独的内存范围。

你可以写

int ( *ptr )[3] = new int[2][3];

在这种情况下,只动态分配了一个二维数组,并且声明的指针ptr 指向数组的第一“行”。也就是只分配了一个范围的内存。

【讨论】:

以上是关于指针数组 (*A)[] 和双指针 **A 之间的区别的主要内容,如果未能解决你的问题,请参考以下文章

对函数调用中的单指针和双指针参数感到困惑

算法基础之数组的增删改查和双指针思想的妙用

算法基础之数组的增删改查和双指针思想的妙用

算法基础之数组的增删改查和双指针思想的妙用

C-函数,数组指针,指针之间的运算

数组和指针,内存之间的关系