【C语言入门教程】4.6 指针 和 数组
数组在内存中以顺序的形式存放,数组的第一个存储单元的地址即数组的首地址。对一维数组来说,直接引用数组名就能获得该数组的首地址。指针变量可以存放于其内容相同的数组首地址,也可以指向某一具体的数组元素。通过这种方式,多维数组也被看作一维数组来操作,简化了数组的操作方式。
4.6.1 指针 与 一维数组
定义一维数组后,可定义一个与其类型相同的指针变量指向该数组。如:
int i[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, *pi; // 定义整型数组 i 和整型指针变量 *pi pi = i; // 将指针变量 *pi 指向数组 i 的首地址
这样 *pi 就指向了数组 i 的第一个单元,该赋值操作等同于 “pi = &i[0]”,pi 与数组 i 的关系型如图 8.7 所示。如果对指针内的地址进行运输,指向“pi +=4”,则 pi 的指向关系随之发生改变,如图 8.8 所示。
因此,可以将 C 语言指针指向一维数组的方法总结为以下 3 条:
(1)、“pi + n”或“i + n”表示数组元素 i[n] 的地址,即“&i[n]”。数组 i 有 10 个元素,n 的取值为 0 至 9,则数组的地址可表示为“pi + 0”至“pi + 9”,或“i + 1”至“i + 9”,与 &i[0] 至 &i[9] 的作用一样;
(2)、知道了数组元素的地址表示方法,“*(pi + n)”;
(3)、指向数组的指针变量也可用数组的下标形式表示为 pi[n],其效果相当于“*(pi + n)”。
4.6.2 指针 与 二维数组
二维数组和其他多维数组都能看作一维数组进行指针操作 。如下例所示:
int i[2][5] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, *pi; // 定义整型二维数组 i 和 整型指针变量 pi pi = i[0]; // 将指针变量 pi 指向数组 i 的首地址
该数组可以视作一个表格,数组 i 与 pi 的关系如图 8.9 所示。如果要访问其中的元素 i[1][2],则可用公式 1 x n + 2 得到该元素的位置,引用时可写作 “*(pi + 1 * n + 2)”。
二维数组的每一行都有一个首地址,如 i[0] 代表 &i[0][0] 的地址,i[1] 代表 i[0][0] 的地址。因此,二维数组也可以看作是 m 个长度为 n 的一维数组,引用二维数组需要将每行的首地址赋给指针变量。如下例,用指针修改二维数组内的元素。
#include <stdio.h> #define M 2 // 定义常量 M,用作二维数组的行数 #define N 4 // 定义常量 N,用作二维数组的列数 int main { int a[M][N], *pa; int i, j; pa = a[0]; // 将指针变量 *pa 指向数组第 1 行的首地址 printf("请输入数组的数值:"); for (i = 0; i < M; i++) { for (j = 0; j < N; j++) { scanf("%d", pa + i * N + j); // 从键盘输入获取数据,保存到数组中 putchar('\0'); // 换行 } } printf("数组内的数值是:"); for (i = 0; i < M * N; i++) { printf("%d\n", *(pa + i)); // 输出数组的值 } return 0; }
该代码首先将数组 a 的第一行首地址赋值给整型指针变量。第一组循环使用数组行和列的关系计算数组元素内存中的位置,给数组 a 的每个元素赋值。第二组循环通过指针变量 pa 间接引用数组 a 内的元素,将所有数组元素的数组输出。
4.6.3 指针 与 字符串数组
字符型指针变量指向字符串数组首地址后,该指针即可当作字符串使用。字符串处理函数通常根据字符串结束符判断该字符串的长度,所以这些函数都会延字符串首地址向后遍历,直到找到字符串结束符。下列用于简单的字符串输入与输出。
char c[20], *pc; // 定义字符型数组 c 与 字符型指针变量 pc pc = c; // 字符型数组 c 的首地址赋给指针变量 pc scanf("%19s", pc); // 通过 pc 间接引用数组 c,为数组 c 初始化 puts(pc); // 输出 pc 所指向的字符串
puts() 函数的作用是将字符串无格式输出,当 *pc 作为其参数时,会内存中将 *pc 所指向的地址开始,到字符串结束符之间的字符输出到终端。假如输入的字符串为“I love China!”,将代码最后一行改为:
puts(pc + 2); // 输出内存中从 pc + 2 开始,到字符串结束符为止的字符串
那么,该程序的输出为 “love China”,因为 puts()函数会跳过字符串数组的前 2 个字符开始处理。如果字符数组中没有字符串结束符,则调用 printf() 或 puts() 输出函数后程序会出错(一直向后处理,输出很多意向不到的字符,直到遇到值为 0 的内存块)。如果字符型指针变量没有初始化,如下例所示:
char *pc; // 字符型数组 c 的首地址赋值给指针变量 pc scanf("%s", pc); // 通过 pc 间接引用数组 c,为数组 c 初始化
这是一种非常危险的用法,因为 pc 的值不可知,pc 可能指向内存中的任意位置。将字符串保存到 pc 所指向的内存单元中,可能造成严重的系统错误。这种用法称之为无源指针,在程序中应避免出现。