C动态分配二维数组

二维数组动态分配内存涉及几个问题:

  • 数组元素是否需要连续;
  • 数组是否规则。

一个声明如下的二维数组所分配的内存是连续的:

int matrix[2][5] = {{1,2,3,4,5},{6,7,8,9,10}};

不过,当我们用malloc这样的函数创建二维数组时,在内存分配上会有几种选择。由于我们可以将二维数组当做数组的数组,因而“内层”的数组没有理由一定要是连续的。如果对这种数组使用下标,数组的不连续对程序员是透明的。

注意 连续性还会影响复制内存等其他操作,内存不连续就可能需要多次复制。

分配可能不连续的内存

下面的代码演示了如何创建一个内存可能不连续的二维数组。首先分配“外层”数组,然后分别用malloc语句为每一行分配。

int rows = 2;
int columns = 5;

int **matrix = (int **) malloc(rows * sizeof(int *));

for (int i = 0; i < rows; i++) {
    matrix[i] = (int *) malloc(columns * sizeof(int));
}

因为分别用了malloc,所以内存不一定是连续的,如图4-15所示。

图4-15:不连续分配

实际的分配情况取决于堆管理器和堆的状态,也有可能是连续的。

分配连续内存

我们会展示为二维数组分配连续内存的两种方法。第一种首先分配“外层”数组,然后是各行所需的所有内存。第二种一次性分配所有内存。

下面的代码片段演示了第一种技术,第一个malloc分配了一个整数指针的数组,一个元素用来存储一行的指针,这就是图4-16中在地址500处分配的内存块。第二个malloc在地址600处为所有的元素分配内存。在for循环中,我们将第二个malloc所分配的内存的一部分赋值给第一个数组的每个元素。

int rows = 2;
int columns = 5;
int **matrix = (int **) malloc(rows * sizeof(int *));
matrix[0] = (int *) malloc(rows * columns * sizeof(int));
for (int i = 1; i < rows; i++)
    matrix[i] = matrix[0] + i * columns;

图4-16:用两次malloc调用分配连续内存

从技术上讲,第一个数组的内存可以和数组“体”的内存分开,为数组“体”分配的内存是连续的。

下面是第二种技术,数组所需的所有内存是一次性分配的:

int *matrix = (int *)malloc(rows * columns * sizeof(int));

分配的情况如图4-17所示。

图4-17:用一次malloc调用分配连续内存

后面的代码用到这个数组时不能使用下标,必须手动计算索引,如下代码片段所示。每个元素被初始化为其索引的积:

for (int i = 0; i < rows; i++) {
    for (int j = 0; j < columns; j++) {
        *(matrix + (i*columns) + j) = i*j;
    }
}

不能使用数组下标是因为我们丢失了允许编译器使用下标所需的“形态”信息。这个概念在传递多维数组讲过了。

实际项目中很少使用这种方法,但它确实说明了二维数组概念和内存的一维本质的关系。便捷的二维数组表示法让这种映射变得透明且更容易使用。

我们已经演示了为二维数组分配连续内存的两种方法,具体使用哪种要看应用程序的需要。不过第二种方法是为“整个”数组分配一块内存。

赞(1)

评论 抢沙发

评论前必须登录!

 

C指针