C 语言编程教程

C 语言 - 首页

C 语言基础

C 语言 - 概述 C 语言 - 特性 C 语言 - 发展历史 C 语言 - 环境设置 C 语言 - 程序结构 C 语言 - Hello World C - 编译过程 C - 注释 C - 标记 C - 关键字 C - 标识符 C - 用户输入 C - 基本语法 C - 数据类型 C - 变量 C - 整数提升 C - 类型转换 C - 类型转换 C - 布尔值

C 语言中的常量和文字

C - 常量 C - 字面量 C - 转义序列 C - 格式说明符

C 语言中的运算符

C - 运算符 C - 算术运算符 C - 关系运算符 C - 逻辑运算符 C - 位运算符 C - 赋值运算符 C - 一元运算符 C - 递增和递减运算符 C - 三元运算符 C - sizeof 运算符 C - 运算符优先级 C - 其他运算符

C 语言中的决策

C - 决策 C - if 语句 C - if...else 语句 C - 嵌套 if 语句 C - switch 语句 C - 嵌套 switch 语句

C 语言中的循环

C - 循环 C - While 循环 C - For 循环 C - Do...while 循环 C - 嵌套循环 C - 无限循环 C - Break 语句 C - Continue 语句 C - goto 语句

C 语言中的函数

C - 函数 C - Main 函数 C - 按值调用函数 C - 按引用调用函数 C - 嵌套函数 C - 可变参数函数 C - 用户定义函数 C - 回调函数 C - return 语句 C - 递归

C 语言中的作用域规则

C - 作用域规则 C - 静态变量 C - 全局变量

C 语言中的数组

C - 数组 C - 数组的属性 C - 多维数组 C - 将数组传递给函数 C - 从函数返回数组 C - 可变长度数组

C 语言中的指针

C - 指针 C - 指针和数组 C - 指针的应用 C - 指针运算 C - 指针数组 C - 指向指针的指针 C - 将指针传递给函数 C - 从函数返回指针 C - 函数指针 C - 指向数组的指针 C - 指向结构体的指针 C - 指针链 C - 指针 vs 数组 C - 字符指针和函数 C - NULL 指针 C - void 指针 C - 悬垂指针 C - 解引用指针 C - Near、Far 和 Huge 指针 C - 指针数组的初始化 C - 指针与多维数组

C 语言中的字符串

C - 字符串 C - 字符串数组 C - 特殊字符

C 语言的结构体和联合

C - 结构体 C - 结构体和函数 C - 结构体数组 C - 自引用结构 C - 查找表 C - 点 (.) 运算符 C - 枚举(或 enum) C - 结构填充和打包 C - 嵌套结构 C - 匿名结构和联合 C - 联合 C - Bit 位字段 C - Typedef

C 语言中的文件处理

C - 输入和输出 C - 文件 I/O(文件处理)

C 语言中的预处理器

C - 预处理器 C - #pragma 编译指示 C - 预处理器操作符 C - 宏 C - 头文件

C 语言中的内存管理

C - 内存管理 C - 内存地址 C - 存储类

C 其他主题

C - 错误处理 C - 可变参数 C - 命令执行 C - 数学函数 C - static 静态关键字 C - 随机数生成 C - 命令行参数

C 语言编程资源

C语言问题与解答答案 C语言快速指南 C语言速查表 C语言实用资源 C语言讨论


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

通常,使用指针访问数组与使用下标表示访问数组非常相似。两者的主要区别在于,使用下标声明的数组会静态分配内存,而使用指针可以动态分配内存。

要将多维数组传递给函数,需要使用指针而不是下标。然而,使用下标数组比使用指针更方便,这对于初学者来说可能比较困难。