注意:本文中使用的示例仅用于解释概念。因此示例可能不完整,可能缺少错误处理等。
在C中使用多维数组时,有以下两种可能的方式。
数组扁平化
在C 中,数组被实现为一个连续的内存块。此信息可用于操作存储在数组中的值,并允许快速访问特定的数组位置。
例如,
int arr[10][10];
int *ptr = (int *)arr ;
ptr[11] = 10;
// this is equivalent to arr[1][0] = 10; assign a 2D array
// and manipulate now as a single dimensional array.
利用数组连续性的技术被称为flattening of arrays。
参差不齐的数组
现在,考虑以下示例。
char **list;
list[0] = "United States of America";
list[1] = "India";
list[2] = "United Kingdom";
for(int i=0; i< 3 ;i++)
printf(" %d ",strlen(list[i]));
// prints 24 5 14
这种类型的实现称为不规则数组,在使用可变大小字符串的地方很有用。流行的方法是在每个维度上都有dynamic-memory-allocation。
注意:命令行参数 (char *argv[]) 仅作为不规则数组传递。
比较扁平和不规则数组
现在,让我们考虑以下代码 sn-p,它比较 flattened 和 ragged 数组。
/* Note: lacks error handling */
int flattened[30][20][10];
int ***ragged;
int i,j,numElements=0,numPointers=1;
ragged = (int ***) malloc(sizeof(int **) * 30);
numPointers += 30;
for( i=0; i<30; i++) {
ragged[i] = (int **)malloc(sizeof(int*) * 20);
numPointers += 20;
for(j=0; j<20; j++) {
ragged[i][j]=(int*)malloc(sizeof(int) * 10);
numElements += 10;
}
}
printf("Number of elements = %d",numElements);
printf("Number of pointers = %d",numPointers);
// it prints
// Number of elements = 6000
// Number of pointers = 631
从上面的例子中,ragged 数组需要631-pointers,换句话说,631 * sizeof(int *) 需要额外的内存位置来指向6000 整数。而flattened 数组只需要一个基指针:即数组的名称足以指向连续的6000 内存位置。
但 OTOH,ragged 数组很灵活。在不知道所需内存位置的确切数量的情况下,您不能奢侈地为最坏的情况分配内存。同样,在某些情况下,所需的内存空间的确切数量仅在运行时才知道。在这种情况下,ragged 数组就派上用场了。
数组的行优先和列优先
C 遵循row-major 对多维数组的排序。 Flattening 的数组可以看作是由于C 中的这一方面的影响。 C 的row-major 顺序的意义在于它适合在编程中进行大多数访问的自然方式。例如,让我们看一个遍历N * M 2D 矩阵的示例,
for(i=0; i<N; i++) {
for(j=0; j<M; j++)
printf(“%d ”, matrix[i][j]);
printf("\n");
}
矩阵中的每一行都是通过快速改变列来逐一访问的。 C 数组以这种自然的方式排列在内存中。相反,请考虑以下示例,
for(i=0; i<M; i++) {
for(j=0; j<N; j++)
printf(“%d ”, matrix[j][i]);
printf("\n");
}
这会比行索引更频繁地更改列索引。正因为如此,这两个代码sn-p在效率上存在很大差异。是的,第一个比第二个效率更高!
因为第一个以 C 的自然顺序(row-major 顺序)访问数组,因此它更快,而第二个需要更多时间来跳转。随着维度数量和元素大小的增加,性能差异会越来越大。
所以在C 中处理多维数组时,最好考虑以上细节!