C语言指针作为函数参数详解(附带实例)
在 C 语言程序中,整型变量、实型变量、字符型变量、数组名和数组元素等均可作为函数参数。此外,指针型变量也可以作为函数参数。
看一个例子,用指针做函数参数,交换两个变量的值。在主函数中,调用自定义函数,利用指针将两个数交换,具体代码如下:
在 swap() 函数的函数体内使用整型变量 tmp 作为中间变量,将两个指针变量所指向的数值进行交换。在 main() 函数内首先获取输入的两个数值,分别传递给变量 x 和 y,调用 swap() 函数将变量 x 和 y 的数值互换。
如果 swap() 函数的形参不是指针而是普通整型,调用 swap() 函数后能实现两数互换吗?将前述程序改成如下形式:
也就是说,在自定义函数体中,即便此时形参的值发生了改变,也无法再传递回来,因此实参的值不会发生变化。这就是为什么普通整型作为函数形参时不能实现 x 和 y 值互换的原因。
通过指针传递参数,可以减少值传递带来的开销,也可以使函数调用不产生值传递。
【实例 1】将 3 个数按降序输出。首先定义交换函数 swap(),参数是指针变量;然后再定义比较大小的函数 exchange(),参数也是指针变量,并且在 exchange() 函数中嵌套交换函数 swap()。在主函数中,调用 exchange() 函数将输入的 3 个数按降序输出,具体代码如下:
程序运行时,通过键盘输入 3 个数 a、b、c,分别将 a、b、c 的地址赋给 q1、q2、q3,调用 exchange() 函数,将指针变量作为实参,将实参变量的值传递给形参变量,此时 q1 和 pt1 都指向变量 a,q2 和 pt2 都指向变量 b,q3 和 pt3 都指向变量c;在 exchange() 函数中又调用了 swap() 函数,当执行 swap(pt1, pt2) 时,pt1 也指向了变量 a,pt2 指向了变量 b,这一过程如下图所示:

图 1 嵌套调用时指针的指向情况
C语言中实参变量和形参变量之间的数据传递是单向的“值传递”方式。指针变量作函数参数也是如此,调用函数不可能改变实参指针变量的值,但可以改变实参指针变量所指向变量的值。
下面介绍如何使用指向数组的指针变量作为函数参数。这里,形参和实参均为指针变量。
【实例 2】计算新生总人数。利用指针变量作函数参数编写程序,根据输入的各班人数求刚入学的初中新生的总人数。
冒泡排序是 C语言中比较经典的例子,也是读者应该牢牢掌握的一种算法,下面分析如何使用指针变量作为函数参数来实现冒泡排序。
【实例 3】实现冒泡排序。冒泡排序的基本思想:如果要对 n 个数进行冒泡排序,则要进行 n−1 轮比较,在第一轮比较中要从后到前进行 n−1 次两两比较,在第 j 轮比较中要进行 n−j 次两两比较。使用指针实现冒泡法排序,代码如下:
【实例 4】水果首字母升序排名。大福超市员工布置水果区,店长要求按照水果名称升序的顺序摆放。各水果的名称及对应单价如下:
编写程序,按照水果名称的首字母将水果升序排序:
下面将通过一个二维数组使用指针变量作为函数参数的实例,加深读者对该部分知识的理解:
【实例 5】找出数组每行中最大数并求和。利用指针作函数参数,找出一个数组每行最大的数,并将这些数相加求和。具体代码如下:
1) 数组名就是数组的首地址,因此例 3 中可以将数组名作为实参传递给指针型形式参数。
2) 当形参为数组时,实参也可以为指针变量。可将例 3 改写成如下形式:
看一个例子,用指针做函数参数,交换两个变量的值。在主函数中,调用自定义函数,利用指针将两个数交换,具体代码如下:
#include <stdio.h> void swap(int *a, int *b) /* 自定义 swap 函数,形参为两个指针 */ { int tmp; tmp = *a; *a = *b; *b = tmp; } int main() { int x, y; int *p_x, *p_y; printf("请输入两个数:\n"); scanf("%d", &x); /* 输入两个数 */ scanf("%d", &y); p_x = &x; p_y = &y; swap(p_x, p_y); /* 调用 swap */ printf("x=%d\n", x); printf("y=%d\n", y); return 0; }程序运行结果为:
请输入两个数:
3 5
x=5
y=3
在 swap() 函数的函数体内使用整型变量 tmp 作为中间变量,将两个指针变量所指向的数值进行交换。在 main() 函数内首先获取输入的两个数值,分别传递给变量 x 和 y,调用 swap() 函数将变量 x 和 y 的数值互换。
如果 swap() 函数的形参不是指针而是普通整型,调用 swap() 函数后能实现两数互换吗?将前述程序改成如下形式:
#include <stdio.h> void swap(int a, int b) /* 自定义 swap 函数,形参为两个整数 */ { int tmp; tmp = a; a = b; b = tmp; } int main() { int x, y; printf("请输入两个数:\n"); scanf("%d", &x); scanf("%d", &y); swap(x, y); /* 调用 swap 函数 */ printf("x=%d\n", x); printf("y=%d\n", y); return 0; }程序运行结果为:
请输入两个数:
3 5
x=3
y=5
也就是说,在自定义函数体中,即便此时形参的值发生了改变,也无法再传递回来,因此实参的值不会发生变化。这就是为什么普通整型作为函数形参时不能实现 x 和 y 值互换的原因。
通过指针传递参数,可以减少值传递带来的开销,也可以使函数调用不产生值传递。
【实例 1】将 3 个数按降序输出。首先定义交换函数 swap(),参数是指针变量;然后再定义比较大小的函数 exchange(),参数也是指针变量,并且在 exchange() 函数中嵌套交换函数 swap()。在主函数中,调用 exchange() 函数将输入的 3 个数按降序输出,具体代码如下:
#include <stdio.h> void swap(int *p1, int *p2) /* 自定义交换函数 swap */ { int temp; temp = *p1; *p1 = *p2; *p2 = temp; } void exchange(int *pt1, int *pt2, int *pt3) /* 自定义排序函数 exchange */ { if (*pt1 < *pt2) swap(pt1, pt2); /* 调用 swap 函数 */ if (*pt1 < *pt3) swap(pt1, pt3); if (*pt2 < *pt3) swap(pt2, pt3); } int main() { int a, b, c, *q1, *q2, *q3; puts("Please input three key numbers you want to rank:"); scanf("%d,%d,%d", &a, &b, &c); q1 = &a; /* 将变量的地址赋给指针变量 */ q2 = &b; q3 = &c; exchange(q1, q2, q3); /* 调用 exchange 函数 */ printf("\n%d,%d,%d\n", a, b, c); return 0; }程序运行结果为:
Please input three key numbers you want to rank:
78,96,56
96,78,56
程序运行时,通过键盘输入 3 个数 a、b、c,分别将 a、b、c 的地址赋给 q1、q2、q3,调用 exchange() 函数,将指针变量作为实参,将实参变量的值传递给形参变量,此时 q1 和 pt1 都指向变量 a,q2 和 pt2 都指向变量 b,q3 和 pt3 都指向变量c;在 exchange() 函数中又调用了 swap() 函数,当执行 swap(pt1, pt2) 时,pt1 也指向了变量 a,pt2 指向了变量 b,这一过程如下图所示:

图 1 嵌套调用时指针的指向情况
C语言中实参变量和形参变量之间的数据传递是单向的“值传递”方式。指针变量作函数参数也是如此,调用函数不可能改变实参指针变量的值,但可以改变实参指针变量所指向变量的值。
下面介绍如何使用指向数组的指针变量作为函数参数。这里,形参和实参均为指针变量。
【实例 2】计算新生总人数。利用指针变量作函数参数编写程序,根据输入的各班人数求刚入学的初中新生的总人数。
#include <stdio.h> void SUM(int *p, int n) /* 自定义 SUM 函数,用于求和 */ { int i, sum = 0; for (i = 0; i < n; i++) /* 循环各班人数并求和 */ sum = sum + *(p + i); /* 相加 */ printf("新生总人数是: %d\n", sum); } int main() { int *pointer, a[10], i; pointer = a; printf("请输入每个班级人数:\n"); for (i = 0; i < 10; i++) /* 输入各班的人数 */ scanf("%d", &a[i]); SUM(pointer, 10); /* 调用 SUM 函数求总人数 */ return 0; }程序运行结果为:
请输入每个班级人数:
42 24 56 60 66 45 54 34 65 50
新生总人数是: 496
冒泡排序是 C语言中比较经典的例子,也是读者应该牢牢掌握的一种算法,下面分析如何使用指针变量作为函数参数来实现冒泡排序。
【实例 3】实现冒泡排序。冒泡排序的基本思想:如果要对 n 个数进行冒泡排序,则要进行 n−1 轮比较,在第一轮比较中要从后到前进行 n−1 次两两比较,在第 j 轮比较中要进行 n−j 次两两比较。使用指针实现冒泡法排序,代码如下:
#include <stdio.h> void order(int *p, int n) /* 自定义冒泡排序函数 order */ { int i, t, j; for (i = 0; i < n - 1; i++) for (j = 0; j < n - 1 - i; j++) /* 判断相邻两个元素的大小 */ if (*(p + j) > *(p + j + 1)) { t = *(p + j); /* 借助中间变量 t 进行值互换 */ *(p + j) = *(p + j + 1); *(p + j + 1) = t; } printf("排序后的数组:"); for (i = 0; i < n; i++) { if (i % 5 == 0) printf("\n"); /* 以每行 5 个元素的形式输出 */ printf("%5d", *(p + i)); /* 输出数组中排序后的元素 */ } printf("\n"); } int main() { int a[20], i, n; printf("请输入数组元素的个数:\n"); scanf("%d", &n); /* 输入数组元素的个数 */ printf("请输入各个元素:\n"); for (i = 0; i < n; i++) scanf("%d", a + i); /* 给数组元素赋初值 */ order(a, n); /* 调用 order 函数 */ return 0; }程序运行结果为:
请输入数组元素的个数: 10 请输入各个元素: 27 25 23 29 21 39 34 38 35 37 排序后的数组: 21 23 25 27 29 34 35 37 38 39前面两个实例都是用一个指向数组的指针变量作函数参数。接下来通过一个实例介绍如何用指向指针的指针作函数参数。
【实例 4】水果首字母升序排名。大福超市员工布置水果区,店长要求按照水果名称升序的顺序摆放。各水果的名称及对应单价如下:
- 苹果(apple):3.50
- 橘子(tangerine):2.50
- 柚子(grapefriu):3.00
- 香蕉(banana):2.00
- 橙子(orange):2.99
- 菠萝(pineapple):4.99
- 葡萄(grape):5.00
- 火龙果(pitaia):6.80
编写程序,按照水果名称的首字母将水果升序排序:
#include <stdio.h> #include <string.h> void sort(char *strings[], int n) /* 自定义排序函数 sort */ { char *temp; int i, j; for (i = 0; i < n; i++) { for (j = i + 1; j < n; j++) { if (strcmp(strings[i], strings[j]) > 0) /* 比较两个字符串的大小 */ { temp = strings[i]; /* 如果前面的字符串比后面的大,则互换 */ strings[i] = strings[j]; strings[j] = temp; } } } } int main() { int n = 8; int i; char **p; /* 定义字符型、指向指针的指针 */ char *name[] = { "apple", "tangerine", "grapefruit", "banana", "orange", "pineapple", "grape", "pitaya" }; p = name; sort(p, n); /* 调用排序函数 sort */ printf("排序后的水果单如下:\n"); for (i = 0; i < n; i++) printf("%s\n", name[i]); /* 输出排序后的字符串 */ return 0; }程序运行结果为:
排序后的水果单如下:
apple
banana
grape
grapefruit
orange
pineapple
pitaya
tangerine
下面将通过一个二维数组使用指针变量作为函数参数的实例,加深读者对该部分知识的理解:
【实例 5】找出数组每行中最大数并求和。利用指针作函数参数,找出一个数组每行最大的数,并将这些数相加求和。具体代码如下:
#include <stdio.h> #define N 4 void max(int (*a)[N], int m) /* 自定义 max 函数,求数组每行的最大元素 */ { int value, i, j, sum = 0; for (i = 0; i < m; i++) { value = *(*(a + i)); /* 将一行中的首个元素赋给 value */ for (j = 0; j < N; j++) if (*(*(a + i) + j) > value) /* 判断其他元素是否小于 value */ value = *(*(a + i) + j); /* 把比 value 大的数重新赋给 value */ printf("第%d 行:最大数是:%d\n", i, value); sum = sum + value; } printf("\n"); printf("每行中最大数相加之和是:%d\n", sum); } int main() { int a[3][N], i, j; int (*p)[N]; p = &a[0]; printf("please input:\n"); for (i = 0; i < 3; i++) for (j = 0; j < N; j++) scanf("%d", &a[i][j]); /* 给二维数组元素赋值 */ max(p, 3); /* 调用 max 函数,指针变量作函数参数 */ return 0; }程序运行结果为:
please input:
96 56 75 85
12 25 84 88
76 65 62 66
第0 行:最大数是:96
第1 行:最大数是:88
第2 行:最大数是:76
每行中最大数相加之和是:260
1) 数组名就是数组的首地址,因此例 3 中可以将数组名作为实参传递给指针型形式参数。
2) 当形参为数组时,实参也可以为指针变量。可将例 3 改写成如下形式:
#include <stdio.h> void order(int a[], int n) { int i, t, j; for (i = 0; i < n - 1; i++) for (j = 0; j < n - 1 - i; j++) /* 判断相邻两个元素的大小 */ if (*(a + j) > *(a + j + 1)) { t = *(a + j); /* 借助中间变量 t 进行值互换 */ *(a + j) = *(a + j + 1); *(a + j + 1) = t; } printf("排序后的数组:"); for (i = 0; i < n; i++) { if (i % 5 == 0) printf("\n"); /* 以每行 5 个元素的形式输出 */ printf("%5d", *(a + i)); /* 输出数组中排序后的元素 */ } printf("\n"); } int main() { int a[20], i, n; int *p; p = a; /* 定义 p 为数组 a 的首地址 */ printf("请输入数组元素的个数:\n"); scanf("%d", &n); /* 输入数组元素的个数 */ printf("请输入各个元素:\n"); for (i = 0; i < n; i++) scanf("%d", p++); /* 给数组元素赋初值 */ p = a; /* 再次定义 p 为数组 a 的首地址 */ order(p, n); /* 调用 order 函数 */ }上述程序中,形参是数组,实参是指针变量。注意倒数第 3 行语句:
p = a; /* 再次定义 p 为数组 a 的首地址 */该语句不可少,如果将其省略,则后面调用 order() 函数时,参数 p 指向的就不再是数组 a 的首地址,从而导致程序运行出错,这点需要读者加以注意。