有关的声明语句及效果
几个例子:
char *a[]; // error: unknown size of a.
char *a1[10]; // type of a1 is "int*[10]", size of a1 is 40.
char (*a2)[]; // type of a2 is "int[0]*", size of a2 is 4.
char (*a3)[10]; // type of a3 is "int[10]*", size of a3 is 4.
数组退化为指针
When an identifier of an array type appears in an expression other than sizeof, address-of (&), or initialization of a reference, it is converted to a pointer to the first array element.
用 new 运算符创建多维数组
When new is used to allocate a multidimensional array of objects, it yields a pointer to the first element of the array, and the resultant type preserves the size of all but the leftmost array dimension. For example:
new float[10][25][10]
yields type float (*)[25][10](pzy:应该也就是 float[25][10]*).
为什么多维数组的非最左维的长度非常重要(因此必须是常量表达式)?
因为它们是指针的数据类型的一部分。比如 int[3]* 和 int[7]* 不属同一类型,这些长度决定了指针的“步距”。假设一个函数接受二维整形数组,可以给它 int[3]* ,但不能给它 int[]* ——指向未知长度的数组的指针是没有意义的,编译器不知道指针加1要偏移多少。
下面的函数接收了 int(*)[] 参数,因为没有指定第二维数组的长度,语句 1 和 2 会导致错误。但是,如果没有这两句,函数是可以被编译的,也就是说 int(*)[] 仍然是个正确的类型(不合理罢了)。
void f(int (*a)[], int rows, int cols)
{
int n = 0;
for (int r = 0; r < rows; ++r)
{
for (int c = 0; c < cols; ++c)
{
a[r][c] = n++; // 语句1, CERROR: int(*)[]:unknown size.
cout << a[r][c] << " "; // 语句2, CERROR: int(*)[]:unknown size.
}
cout << endl;
}
}
对于上面的函数,它应该把参数 a 声明为 int(*)[COLS],其中 COLS 为常数表达式。如果不能在编译时确定 COLS,函数 f 的参数列表就应该是类似这个样子了:(int **a, int rows, int cols) 或 (int *a, int rows, int cols),前者使用数组的数组,后者使用一维数组。
示例代码
演示了几种使用动态二维数组的方式。
#1 使用“多级”数组,它可以做成钜齿数组。
#1-2 既可看成 #1 的变形,也可看成 #3 的扩展。作为 #1 的变形,它能保证各子数组是连续的,但无法做成钜齿数组。作为 #3 的扩展,它相当于引入了一组书签——如果把目标数组看作一本书(每一页代表一个数组元素)的话。
#2 无视多维数组的概念, 用一维数组以行为主序连续存放子数组,通过下标算术访问元素:
*((array-name) + (subscript1* max2 * max3...maxn)
+ subscript2 * max3...maxn)
. . . + subscriptn))
#3 真正的二维数组(为什么有人说C++里没有真正的多维数组?)。arr4是指向数组的指针,它的步距是一个数组。值得注意的是多维数组的释放方式,就是简单的 delete[]。
总结起来,真多维数组的使用是最简便的,但是它有一个严重的限制是,它的高维子数组要求常量尺寸。
// want int[NCOLS,NROWS].
const int NCOLS = 7;
int NROWS = 3;
// #1: array of array.
// allocate.
int **arr1 = new int*[NROWS]; // memory for int*.
for (int r = 0; r < NROWS; ++r)
arr1[r] = new int[NCOLS]; // memory for int.
// access:arr1[r][c].
// destroy:upwards.
for (int r = 0; r < NROWS; ++r)
delete []arr1[r]; // memory for int.
delete []arr1; // memory for int*.
arr1 = NULL;
// #1-2: keep array's contents contiguous.
// allocate.
int **arr2 = new int*[NROWS]; // memory for int*.
arr2[0] = new int[NROWS * NCOLS]; // memory for int.
for (int r = 1; r < NROWS; ++r)
arr2[r] = arr2[r - 1] + NCOLS;
// access: arr2[r][c].
// destroy:upwards.
delete []arr2[0]; // memory for int.
delete []arr2; // memory for int*.
arr2 = NULL;
// #2: singly dimensioned array,
// access by subscript calculations.
// allocate.
int *arr3 = new int[NROWS * NCOLS]; // arr3 == arr2[0].
// access: arr3[c + r * NCOLS].
// destroy.
delete []arr3; // memory for int.
arr3 = NULL;
// #3: pointer to array.
// allocate.
int (*arr4)[NCOLS] = new int[NROWS][NCOLS];
// NCOLS must be constant expression.
// Type of arr4 is int[NCOLS]* - a pointer.
// access: arr4[r][c];
// destroy:upwards.
delete []arr4; // memory for int*.
arr4 = NULL;