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 的首地址,从而导致程序运行出错,这点需要读者加以注意。
ICP备案:
公安联网备案: