Cprogramming 简明教程
Pointers and Multidimensional Arrays in C
在 C 语言中, array 是一个值集合,这些值类型相似,存储在连续的内存位置中。数组(一维或多维)中的每个元素由一个或多个唯一的整数索引标记。
另一方面, pointer 存储的是 variable 的地址。数组中第 0 个元素的地址是 pointer of the array 。您可以使用“解引用运算符”来访问指针引用的值。
可以在 C 语言中声明一维、二维或多维数组。术语“维度”指的是标识集合中的元素所需的索引数量。
Pointers and One-dimensional Arrays
在一维数组中,每个元素由一个整数标识。
int a[5] = {1, 2, 3, 4, 5};
此处,数字“1”位于第 0 个索引,“2”位于索引 1,依此类推。
一个存储第 0 个元素地址的变量是其指针−
int *x = &a[0];
简单来说,数组的名称也指向第 0 个元素的地址。因此,您还可以使用此表达式−
int *x = a;
Example
由于指针的值将以数据类型的大小递增,“x++”将指针移动到数组中的下一个元素。
#include <stdio.h>
int main(){
int arr[] = {1, 2, 3, 4, 5};
int length = sizeof(arr) / sizeof(arr[0]);
int i = 0;
int *ptr = arr;
while (i < length){
printf("arr[%d]: %d \n", i, *(ptr + i));
i++;
}
return 0;
}
当你运行这段代码时,它将产生以下输出:
arr[0]: 1
arr[1]: 2
arr[2]: 3
arr[3]: 4
arr[4]: 5
Pointers and Two-dimensional Arrays
如果一维数组像元素列表,那么二维数组就像表格或矩阵。
二维数组中的元素被认为在逻辑上排列成行和列。因此,任何元素的位置由两个索引确定,即其行号和列号。行索引和列索引都从“0”开始。
int arr[2][2];
这样的数组表示为−
Col0 |
Col1 |
Col2 |
|
Row0 |
arr[0][0] |
arr[0][1] |
arr[0][2] |
Row1 |
arr[1][0] |
arr[1][1] |
arr[1][2] |
Row2 |
arr[2][0] |
arr[2][1] |
arr[2][2] |
值得注意的是,表格排列仅仅是一种逻辑表示。编译器分配的是连续字节块。在 C 语言中,数组分配按行优先的方式进行,这意味着元素按行方式读入数组。
此处,我们声明一个包含三行四列的二维数组(第一个方括号中的数字始终表示行数),如下所示:
int arr[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
编译器将按行优先的顺序为上述二维数组分配内存。假设数组的第一个元素位于地址 1000,并且类型“int”的大小为 4 字节,那么数组元素将获得以下分配内存位置:
Row 0 |
Row 1 |
Row 2 |
|
Value |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
Address |
1000 |
1004 |
1008 |
1012 |
1016 |
1020 |
1024 |
1028 |
1032 |
1036 |
我们将使用地址运算符 & 将数组 num 的第一个元素的地址分配给指针 ptr。
int *ptr = &arr[0][0];
Example 1
如果指针递增 1,它将移动到下一个地址。“3×4”数组中的所有 12 个元素都可以通过循环访问,如下所示:
#include <stdio.h>
int main(){
int arr[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12},
};
// pointer ptr pointing at array num
int *ptr = &arr[0][0];
int i, j, k = 0;
// print the elements of the array num via pointer ptr
for (i = 0; i < 3; i++){
for (j = 0; j < 4; j++){
printf("%d ", *(ptr + k));
k++;
}
printf("\n");
}
return 0;
}
当你运行这段代码时,它将产生以下输出:
1 2 3 4
5 6 7 8
9 10 11 12
通常情况下,数组的任何元素的地址都通过以下公式获得:
add of element at ith row and jth col = baseAddress + [(i * no_of_cols + j) * sizeof(array_type)]
在我们的 3×4 数组中:
add of arr[2][4] = 1000 + (2*4 + 2)*4 = 1044
你可以参考上述数字,它确认 “arr[3][4]” 的地址是 1044。
Example 2
使用 dereference pointer 获取地址中的值。让我们使用这个公式,通过指针遍历数组:
#include <stdio.h>
int main(){
// 2d array
int arr[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
int ROWS = 3, COLS = 4;
int i, j;
// pointer
int *ptr = &arr[0][0];
// print the element of the array via pointer ptr
for (i = 0; i < ROWS; i++){
for (j = 0; j < COLS; j++) {
printf("%4d ",*(ptr + (i * COLS + j)));
}
printf("\n");
}
return 0;
}
当你运行这段代码时,它将产生以下输出:
1 2 3 4
5 6 7 8
9 10 11 12
Pointers and Three-dimensional Arrays
三维数组是二维数组的数组。这种数组使用三个下标声明:
int arr [x] [y] [j];
该数组可视为 “x” 层表格,每个表格具有 “x” 行和 “y” 列。
3D 数组的示例为:
int arr[3][3][3] ={
{ {11, 12, 13}, {14, 15, 16}, {17, 18, 19} },
{ {21, 22, 23}, {24, 25, 26}, {27, 28, 29} },
{ {31, 32, 33}, {34, 35, 36}, {37, 38, 39} },
};
指向 3D 数组的指针可以声明为:
int * ptr = &arr[0][0][0];
了解到数组本身的名称是第 0 个元素的地址,我们可以将 3D 数组的指针写入为:
int * ptr = arr;
“x” 行和 “y” 列的每一层占据:
x * y * sizeof(data_type)
字节数。假设上文声明的 3D 数组 “arr” 分配的内存从地址 1000 开始,第二层(“i = 1”)从 1000 +(3 × 3)× 4 = 1036 字节位置开始。
ptr = Base address of 3D array arr
如果 JMAX 是行数,KMAX 是列数,则第 1 个切片中第 0 行第 0 列的元素的地址为:
arr[1][0][0] = ptr + (1 * JMAX * KMAX)
为了获得第 i 个切片的第 j 行第 k 列的元素的值,公式可表示为:
arr[i][j][k] = *(ptr + (i * JMAX*KMAX) + (j*KMAX + k))
Example: Printing a 3D Array using Pointer Dereferencing
让我们使用此公式,通过指针取消引用来打印 3D 数组:
#include <stdio.h>
int main(){
int i, j, k;
int arr[3][3][3] = {
{ {11, 12, 13}, {14, 15, 16}, {17, 18, 19} },
{ {21, 22, 23}, {24, 25, 26}, {27, 28, 29} },
{ {31, 32, 33}, {34, 35, 36}, {37, 38, 39} },
};
int JMAX = 3, KMAX = 3;
int *ptr = arr; // &arr[0][0][0];
for(i = 0; i < 3; i++){
for(j = 0; j < 3; j++){
for(k = 0; k < 3; k++){
printf("%d ",*(ptr+(i*JMAX*KMAX)+(j*KMAX+k)));
}
printf("\n");
}
printf("\n");
}
return 0;
}
当你运行这段代码时,它将产生以下输出:
11 12 13
14 15 16
17 18 19
21 22 23
24 25 26
27 28 29
31 32 33
34 35 36
37 38 39
通常,使用指针访问数组与使用下标表示访问数组非常相似。两者的主要区别在于,带有下标的数组声明会静态分配内存,而我们可以使用指针进行动态内存分配。
要将 multi-dimensional array 传递给函数,需要使用指针而不是下标。但是,使用下标数组比使用指针更方便,这对于初学者来说可能很困难。