为什么我在堆和堆栈上分配的数组之间看到不同的行为?

时间:2017-09-13 20:45:15

标签: c++ arrays memory-management

我正在检查C ++中两个2D数组的行为,一个是从堆栈中分配的,另一个是从堆中分配的。

我创建了两个相同形状的2D数组,并用一些数据填充这些数组。然后我尝试用两种不同的方法读取数组,第一种方法是使用简单的数组索引格式“Arr [ROW] [COLUMN]”。然后我使用指针取消引用读取数组,我得到堆分配数组的两个不同结果,但堆栈分配数组的结果相同。我试图理解为什么结果不同。如果有人能提供任何澄清,我将不胜感激。提前谢谢。

我正在运行的代码如下:

#include <iostream>

using namespace std;

int main(){

    int rows = 6;
    int columns = 3;

    // allocate from the stack.
    double q[rows][columns];

    // allocate from the heap.
    double ** a;
    a = new double*[rows];

    for(int i = 0; i < rows; ++i){
        a[i] = new double[columns];
    }

    // populate the arrays.
    for(int i = 0; i < rows; ++i){
        for(int j = 0; j < columns; ++j){
            a[i][j] = columns*i+j;
            q[i][j] = columns*i+j;
        }
    }

    cout << "*****************" << endl;
    cout << "Array indexing method." << endl;
    cout << "*****************" << endl;

    // print the heap allocated array using array indexing.
    for(int i = 0; i < rows; ++i){
        for(int j = 0; j < columns; ++j){
            cout << a[i][j] << '\t';
        }
        cout << endl;
    }

    cout << "*****************" << endl;

    // print the stack allocated array using array indexing.
    for(int i = 0; i < rows; ++i){
        for(int j = 0; j < columns; ++j){
            cout << q[i][j] << '\t';
        }
        cout << endl;
    }

    cout << "*****************" << endl;
    cout << "Pointer dereferencing method." << endl;
    cout << "*****************" << endl;

    // print the heap allocated array.
    for(int i = 0; i < rows; ++i){
        for(int j = 0; j < columns; ++j){
            cout << *(&a[0][0] + columns*i + j) << '\t';
        }
        cout << endl;
    }

    cout << "*****************" << endl;

    // print the stack allocated array.
    for(int i = 0; i < rows; ++i){
        for(int j = 0; j < columns; ++j){
            cout << *(&q[0][0] + columns*i + j) << '\t';
        }
        cout << endl;
    }
    cout << "*****************" << endl;

    // release the memory allocated to the heap.
    for(int i = 0; i < rows; ++i){
        delete[] a[i];
    }

    delete a;

    return 0;
}

我得到的结果是:

*****************
Array indexing method.
*****************
0       1       2
3       4       5
6       7       8
9       10      11
12      13      14
15      16      17
*****************
0       1       2
3       4       5
6       7       8
9       10      11
12      13      14
15      16      17
*****************
Pointer dereferencing method.
*****************
0       1       2
0       3       4
5       0       6
7       8       0
9       10      11
0       12      13
*****************
0       1       2
3       4       5
6       7       8
9       10      11
12      13      14
15      16      17
*****************

我可以看到,在第三个输出块中,堆分配的数组没有被正确读取,但堆栈分配的数组是。

再次感谢。

3 个答案:

答案 0 :(得分:11)

&q[0][0]为您提供指向包含rows x columns双打的块中第一个双精度的指针。虽然&a[0][0]为您指定了包含columns的块中的第一个双精度(您已使用a[0] = new double[columns];分配它,但请记住?)。因此,访问它columns*i + j将超出范围,并将触发未定义的行为。

答案 1 :(得分:3)

在这种情况下,数组通常是一个单独的分配。数组值q[1][0]位于q[0][columns-1]之后。指针实现为堆中的每一行分配一个不相交的内存块。通过引用超出第一行结尾,您处于未定义行为区域。

答案 2 :(得分:2)

基于堆栈的阵列是完全连续的内存。基于堆的一个是每行一个分配,加上一个分配来保存所有这些分配。每个分配的内存可以按任何顺序分散在整个堆中,它们之间有任意数量的空间。您不能假设在第一行分配的基础上添加偏移量是有效的,如果您超过其结束,并且通过在前一次分配结束时走得足够远来尝试达到单独的分配肯定是一个错误。

如果您想要执行您正在尝试的操作,请将新的[]表达式更改为扁平化为单个数组:

double * q = new [rows * columns];

然后你可以索引它,好像它是一个二维数组。

此外,在您的原始帖子中,您在a上使用了错误类型的删除调用,这也是一个数组,需要作为一个删除(后面带[]。