C 语言中的指针和多维数组
在 C 语言中,数组是存储在连续内存位置的相似类型值的集合。数组(一维或多维)中的每个元素都由一个或多个唯一的整数索引标识。
另一方面,指针存储的是变量的地址。数组中第 0 个元素的地址是数组的指针。您可以使用"解引用运算符"来访问指针指向的值。
您可以在 C 语言中声明一维、二维或多维数组。术语"维度"是指标识集合中元素所需的索引数量。
指针和一维数组
在一维数组中,每个元素由一个整数标识:
int a[5] = {1, 2, 3, 4, 5};
这里,数字"1"位于索引 0,"2"位于索引 1,依此类推。
存储第 0 个元素地址的变量是其指针 -
int *x = &a[0];
简单来说,数组的名称也指向第 0 个元素的地址。因此,您也可以使用以下表达式 -
int *x = a;
示例
由于指针的值会根据数据类型的大小递增,"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 ", i, *(ptr + i)); i++; } return 0; }
输出
运行此代码时,将产生以下输出 -
arr[0]: 1 arr[1]: 2 arr[2]: 3 arr[3]: 4 arr[4]: 5
指针和二维数组
如果说一维数组就像一个元素列表,那么二维数组就像一个表格或矩阵。
二维数组中的元素可以被认为是按行和列逻辑排列的。因此,任何元素的位置都由两个索引决定:行号和列号。行号和列号均从"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 | 1040 | 1044 |
我们将使用 & 运算符的地址将数组 num 的第一个元素的地址赋值给指针 ptr。
int *ptr = &arr[0][0];
示例 1
如果指针加 1,它将移动到下一个地址。"34"数组中的所有 12 个元素可以通过循环访问,如下所示 -
#include <stdio.h> int main(){ int arr[3][4] = { {1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, }; // 指向数组 num 的指针 ptr int *ptr = &arr[0][0]; int i, j, k = 0; // 通过指针 ptr 打印数组 num 的元素 for (i = 0; i < 3; i++){ for (j = 0; j < 4; j++){ printf("%d ", *(ptr + k)); k++; } printf(" "); } 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)]
在我们的 34 数组中,
add of arr[2][4] = 1000 + (2*4 + 2)*4 = 1044
您可以参考上图,它确认"arr[3][4]"的地址为 1044。
示例 2
使用取消引用指针获取该地址的值。让我们使用此公式借助指针遍历数组 -
#include <stdio.h> int main(){ // 二维数组 int arr[3][4] = { {1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12} }; int ROWS = 3, COLS = 4; int i, j; // 指针 int *ptr = &arr[0][0]; // 通过指针 ptr 打印数组的元素 for (i = 0; i < ROWS; i++){ for (j = 0; j < COLS; j++) { printf("%4d ",*(ptr + (i * COLS + j))); } printf(" "); } return 0; }
输出
运行此代码时,将产生以下输出 -
1 2 3 4 5 6 7 8 9 10 11 12
指针和三维数组
三维数组是由二维数组构成的数组。此类数组用三个下标声明 -
int arr [x] [y] [j];
此数组可视为"x"层表,每层表有"x"行和"y"列。
三维数组的一个例子是 -
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 * ptr = &arr[0][0][0];
已知数组本身的名称是第 0 个元素的地址,因此我们可以将三维数组的指针写为 −
int * ptr = arr;
每层"x"行和"y"列占用 −
x * y * sizeof(data_type)
字节数。假设上面声明的三维数组"arr"的内存分配从地址 1000 开始,第二层(i = 1)从 1000 + (3 3) 4 = 1036 字节的位置开始。
ptr = 三维数组 arr 的基址
设 JMAX 为行数,KMAX 为列数,则第一个切片的第 0 行 0 列元素的地址为 −
arr[1][0][0] = ptr + (1 * JMAX * KMAX)
获取第 0 行元素值的公式为第 i 个切片的第 j 行和第 k 列可以表示为 −
arr[i][j][k] = *(ptr + (i * JMAX*KMAX) + (j*KMAX + k))
示例:使用指针解引用打印三维数组
让我们使用此公式,借助指针解引用打印三维数组 −
#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(" "); } printf(" "); } 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
通常,使用指针访问数组与使用下标表示访问数组非常相似。两者的主要区别在于,使用下标声明的数组会静态分配内存,而使用指针可以动态分配内存。
要将多维数组传递给函数,需要使用指针而不是下标。然而,使用下标数组比使用指针更方便,这对于初学者来说可能比较困难。