下面的清单创建了一个二维动态数组。
#include <iostream>
#define COLS 3
#define ROWS 3
int ** create2d()
{
int index = 1;
int** arr = new int*[COLS];
for (size_t i = 0; i < COLS ; i++)
{
arr[i] = new int[ROWS];
for (size_t j = 0; j < ROWS ; j++)
{
arr[i][j] = index++;
printf("%d (%d) [%d][%d]\t", arr[i][j], &arr[i][j], i,j);
}
printf("\n");
}
return arr;
}
int main()
{
int** arr = create2d();
for (int i = 0; i < COLS; i++)
{
delete[] arr[i];
}
delete[] arr;
return 0;
}
字符串
输出
./main
1 (34979536) [0][0] 2 (34979540) [0][1] 3 (34979544) [0][2]
4 (34980608) [1][0] 5 (34980612) [1][1] 6 (34980616) [1][2]
7 (34980640) [2][0] 8 (34980644) [2][1] 9 (34980648) [2][2]
型
从输出中,我们看到元素的增量和数组元素的地址匹配。COLS
和ROWS
名称是否正确?
如果是这样,这是否意味着在arr[p][q]
中,左手索引(p
)总是可以被认为是列,而右侧索引(q
)总是可以被认为是行?
2条答案
按热度按时间0s7z1bwu1#
在C和C中,为什么我们说数组是按行为主的顺序存储的?
在数学中,二维矩阵中下标的传统顺序是先放行下标,后放列下标。对于给定的矩阵
A
,在第m行和第n列中的元素被表示为A(m,n)
。正是这种约定迫使我们得出结论,C和C中的二维数组是以行为主的顺序存储的。当然,在C和C++中,并没有实际的二维数组。最接近的类比是
array of arrays
。这就是我们所说的“二维数组”的含义。在下面的声明中,
A
就是这样一个数组。它的类型是array of arrays of int
。字符串
更准确地说,它的类型是“M个元素的数组,其中每个元素都是int类型的N个元素的数组”。这是一个口。
下面是几个类型别名:
型
M_by_N_matrix
类型与int[M][N]
类型相同。你可以在一个程序中使用std::is_same_v
来测试。使用括号运算符[]引用
M_by_N_matrix
的元素两次,例如A[m][n]
。运算符[]的规则要求从左到右的绑定顺序,因此这等价于(A[m])[n]
。意思是“数组A[m]
的第n个元素,其中A[m]
是数组A
的第m个元素”。现在,我们可以看到下标
m
与行关联的原因:我们希望C和C中的A[m][n]
与数学中的A(m,n)
具有相同的含义。第一个下标是行下标。这只是个惯例,仅此而已好吧,那我们怎么才能达到行大序呢?
答案就在于数组的定义。数组的元素必须储存在连续的位置(可能会加上填补以满足对齐需求)。首先存储元素0,然后是元素1,依此类推。
在上面的示例中,数组
A
的元素是数组。因为M = 3,所以有三个。它们被连续存储:A[0] A[1] A[2]
但是我们在上面已经决定了第一个下标是行下标。A[0]是一个包含N个整数的数组,它对应于我们的数学矩阵中的第0行。类似地,A[1]是第1行,A[2]是第2行。根据定义,这是行主顺序。
行0行1行2
所以,你有它:在C和C中,“二维数组”都是以行为主的顺序存储的。
顺便说一下,下面的程序返回0。
型
vdgimpew2#
每次输出'\n'都是结束一行。因此,内部循环打印一行的元素。要做到这一点,列索引的范围必须在0和我称之为n_cols之间。保持行索引不变,而改变列索引。因此,您称为ROWS的东西实际上是n_cols。
外部循环的每次迭代打印一行。您打印多少行?我将其称为n_rows,但您将其标记为COLS。
所以我会说,不,你对ROWS和COLS的命名是不正确的。
如果将COLS更改为2,但将ROWS保持为3,则输出将包含两行,而不是两列。
你建议你的j索引是一个行索引,但是你第二次打印它,* 这里打印列索引是正常的。* 基本上,你已经转置了名字,足以说服自己矩阵是以列优先的顺序存储的。