先回顾new在堆上分配内存,见我的博文点击打开链接 C/C++的数据类型判断的最后总结的:如果new后边的类型是非数组类型,那么返回的类型是这种类型的指针(最内层的指针,是其本质);如果new后边的类型是数组,那么返回的类型是数组退化为指针之后的类型。关于退化,见我的博文点击打开链接中关于C/C++赋值中的退化规则。
malloc的返回值是void* 类型的泛型指针,需要强制类型转换为其它指针类型来赋值。
分配连续的内存给二维数组:
int rows = 10;
int columns = 5;
int *p[10]; //p是指针数组 数组在三中情况之外都相当于指针常量
int **matrix = p;
matrix[0] = (int*)malloc(rows*columns*sizeof(int));
for (int i = 1; i < rows; ++i)
{
matrix[i] = matrix[0] + i*columns;
}
或者这样,跟要求连续空间的二维数组保持一致:
int rows = 10;
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;
cout << matrix[i] << endl;
}
需要注意的是,这里一定要有p这个数组,而不能简单的定义一个指针变量
int *p;
int **matrix = &p;
原因是matrx[1] , matrix[2]...都发生了越界,他们都不是定义的变量的地址(指针),不能去改写。
分配非要求连续空间的二维数组:
int rows = 10;
int columns = 5;
int** matrix = (int**)malloc(rows*sizeof(int*)); //在堆上构建一个中间数组,中间数组的每个元素来管理一行
for (int i = 0; i < rows; ++i)
{
matrix[i] = (int*)malloc(columns*sizeof(int));
}
或者像上边一样,管理的一个一维数组在栈上:
int rows = 10;
int columns = 5;
int *p[10];
int ** matrix = p;
for (int i = 0; i < rows; ++i)
{
p[i] = (int*)malloc(columns*sizeof(int));
}
可以看到无论哪种方式,都需要借助一个中间层数组来管理。于是可以看到C++中new 操作符的强大优势,分配二维数组,直接new int [10][5], 简单不容易出错, hiahia.
二维数组若是退化,会退化为数组指针,C/C++中的数组本身是按行优先的线性存储,二维数组名是指向第0行的数组指针,终极的写法如下:
typedef int(*Arr)[5];
Arr p = (Arr)malloc(sizeof(int)* 10 * 5); //p是数组指针,指向的一维的维数是5
//malloc分配的是连续的线性空间 一共有10个这样的一维数组
其实无论怎样,最终都需要malloc() 出 sizeof(int)*rows*columns这么多空间 ,无论是一维,二维,还是三维数组,最终都是线型的存储在内存中,问题的关键在于能不能通过matrix[i][j] 这样形式,使用下标能够正确的索引。
常见的错误写法如下:
int rows = 10;
int columns = 5;
int** matrix = (int**)malloc(rows*columns*sizeof(int));
for (int i = 1; i < rows; ++i)
{
matrix[i] = matrix[0] + i*columns;
}
matrix[9][9] = 0;
这里的matrix[0]是分配空间的最开始四个字节,由于matrx的类型是int**,所以这四个字节此时被当做int*来解释了,不过由于这四个字节的内容未知,这就是错误的根源!