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 传递给函数,需要使用指针而不是下标。但是,使用下标数组比使用指针更方便,这对于初学者来说可能很困难。