单指针数组和双指针数组之间的区别

Difference between single-pointer and double-pointer arrays

本文关键字:指针 数组 之间 区别 单指针      更新时间:2023-10-16

下面的代码打印动态分配的数组的地址。当新行开始时,打印的地址会略有不同。如果我使用静态数组,地址完全相同,数组元素一个接一个。原因是什么?

void func(int* a, int** b)
{
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < n; j++)
        {
            cout << &a[i * n + j]<< " " << &b[i][j]<< endl;
        }
        cout << endl;
    }
}
int main()
{
    int** a;
    a = new int*[n];
    for (int i = 0; i < n; i++)
    {
        a[i] = new int[n];
    }
    func(a[0], a);

单指针数组和双指针数组之间的区别

您可以在代码中看到差异:

int** a;         // a is a pointer of type int**
a = new int*[n]; // a points to an array of pointers of type int*

和:

for (int i = 0; i < n; i++)
{
    a[i] = new int[n]; // a[i] points to an array of ints

你有它。

当新行开始时,打印的地址会略有不同。如果我使用静态数组,地址完全相同,数组元素一个接一个。

据推测,您正在谈论数组的静态数组,并将其与您拥有的指针数组进行比较。

数组的元素在内存中连续分配。在静态数组的情况下,外部数组的元素是静态数组,它们连续存储在内存中。在指针数组的情况下,外部数组的元素是指针,它们也连续存储在内存中。另一方面,动态分配的int数组不会存储在外部数组中。它们的存储位置是实现定义的,通常与保存指针的数组的位置无关。

也:

cout << &a[i * n + j]<< " " << &b[i][j]<< endl;
在这里,您访问的数组边界

超出了参数所指向的数组边界a该参数由a[0] in main 指向,并且当i为非零时,其长度为 n 小于 i * n + j。结果是技术上未定义的行为,打印的地址绝对不是您分配的数组元素。

Code below prints addresses of dynamically allocated array. 
Printed addresses become slightly different, when new line starts. If
I use static array, addresses are exactly the same, and array elements
are going one after another. What's the reason?
原因是每次调用时,

以下调用都会生成一个新地址,并且不能保证该地址是连续的。 a[i] = 新 int[n];

国际A[

10][20]

这是一个静态数组,将具有连续的内存分配。

这就是你创建矩阵的方式。没有理由假设a[0] + 1 * n + j等于a[1] + ja[0]a[1]是独立分配的。

这是一种一次获取整个矩阵的方法。

int** a = new int*[n]; // allocate the row indexes
a[0] = new int[n * n]; // allocate the entire matrix
for (int i = 1; i < n; i++)
{
    a[i] = a[0] + i * n; // assign the row indexes. NOTE: a[0] is already assigned.
}

使用此分配,a[0] + 1 * n + j等于 a[1] + j


静态数组隐式分配整个矩阵分配,这就是您的代码适用于它们的原因。

你在建模中使用了两个相互冲突的实现矩阵:在main中,您使用数组的数组,每一行分离记忆;在func中,你假设一个平面数组的 rows * columns 个元素,将两个索引映射到一个。你必须选择一个或另一个。

当然,在C++中,你会编写一个Matrix类来封装这个选择。 例如,如果您想要单个平面阵列(通常最好(,你会写这样的东西:

class Matrix
{
    int myRowCount;
    int myColumnCount;
    std::vector<int> myData;
public:
    Matrix( int rows, int columns )
        : myRowCount( rows )
        , myColumnCount( columns )
        , myData( rows * columns )
    {
    }
    int& operator()( int row, int column )
    {
        assert( row >= 0 && row < myRowCount
                && column >= 0 && column < myColumnCount );
        return myData[row * myColumnCount + column];
    }
    int const& operator()( int row, int column ) const
    {
        assert( row >= 0 && row < myRowCount
                && column >= 0 && column < myColumnCount );
        return myData[row * myColumnCount + column];
    }
};

你会注意到我用了std::vector而不是做任何事情动态分配我自己。 在这种情况下,区别不是巨大,但在实践中,没有一个有经验的C++程序员会使用新的数组,除非在非常特殊的情况下;一有时想知道为什么它甚至在语言中。

您还会注意到,我已经重载了 () 运算符索引。 您不能为 [] 提供两个索引,这是该问题的解决方案之一。 或者 operator[]将采用单个索引,并返回一个代理对象,该对象也将采用单个索引。 虽然这是我更喜欢的解决方案,它更复杂。