1、C程序设计程序设计Programming in C函数之间数据交换的高效方法函数之间数据交换的高效方法1、指针作为函数参数2、数组作为函数参数的实质C程序设计程序设计37.5 指针与函数指针与函数指针最重要的应用是作为函数参数,它使得被调函数除了返回值之外,能够将更多的运算结果返回到主调函数中,即指针是函数参数传递的重要工具。47.5.1 指针作为函数参数指针作为函数参数函数参数不仅可以是基本类型等普通对象,还可以是数组、指针变量。57.5.1 指针作为函数参数指针作为函数参数1指针变量作为函数形参函数形参可以是指针类型,一般形式为:相应的,调用函数时必须用相同指向类型的指针(或地址)作为函数
2、实参。返回类型函数名(指向类型*指针变量名,.)函数体67.5.1 指针作为函数参数指针作为函数参数【例7.18】输入a和b两个整数,按从小到大的顺序输出a、b。77.5.1 指针作为函数参数指针作为函数参数例7.181 #include 2 void swap(int *p1, int *p2)3 4 int t;5 t=*p1 , *p1=*p2, *p2=t; /交换*p1和*p26 7 int main()8 9 int a, b;10 scanf(%d%d,&a,&b); /输入11 if (ab) swap(&a, &b);12 printf(min=%d,max=%dn,a,b)
3、; /输出13 return 0;14 87.5.1 指针作为函数参数指针作为函数参数例7.181 #include 2 void swap(int *p1, int *p2)3 4 int t;5 t=*p1 , *p1=*p2, *p2=t; /交换*p1和*p26 7 int main()8 9 int a, b;10 scanf(%d%d,&a,&b); /输入11 if (ab) swap(&a, &b);12 printf(min=%d,max=%dn,a,b); /输出13 return 0;14 min=123,max=456456 123程序运行屏幕97.5.1 指针作为函数
4、参数指针作为函数参数swap函数的作用是交换a和b的值。由于swap函数的两个形参是指向int型的指针变量,因此调用swap函数时实参必须是指向int型的指针或地址,例如:如果换成是错误的。swap(&a, &b); /正确实参&a、&b为int*,与形参要求一致swap(a, b); /错误实参a、b为int,与形参指针类型要求不一致107.5.1 指针作为函数参数指针作为函数参数即便是函数参数,C语言不会对任何指针类型做隐式类型转换。117.5.1 指针作为函数参数指针作为函数参数例7.181 #include 2 void swap(int *p1, int *p2)3 4 int t;
5、5 t=*p1 , *p1=*p2, *p2=t; /交换*p1和*p26 下面分析swap函数是如何交换a和b的值。调用swap时,实参分别是变量a和b的地址,根据函数参数的值传递规则,swap函数的形参指针变量p1得到了变量a的地址,p2得到了变量b的地址。换句话说,此时p1指向变量a,p2指向变量b,*p1等价于a,*p2等价于b。127.5.1 指针作为函数参数指针作为函数参数例7.181 #include 2 void swap(int *p1, int *p2)3 4 int t;5 t=*p1 , *p1=*p2, *p2=t; /交换*p1和*p26 那么实际效果相当于结果是a
6、和b的值交换了。t=*p1, *p1=*p2, *p2=t;t=a, a=b, b=t;137.5.1 指针作为函数参数指针作为函数参数可不可以直接在swap函数写“t=a , a=b, b=t;”交换a和b呢?答案是不可以。因为a和b是main函数定义的局部变量,其作用域仅在main函数内部有效,对其他任何函数来说不可见。void swap(.)int t;t=a , a=b, b=t;147.5.1 指针作为函数参数指针作为函数参数如果将swap函数写成在main函数的调用写成void swap(int p1, int p2)int t;t=p1, p1=p2, p2=t;swap(a,
7、b);157.5.1 指针作为函数参数指针作为函数参数能不能实现a和b交换呢?答案是不行。因为这样写的含义是:a和b的值传递给了形参p1和p2,p1和p2是a和b的副本,在swap函数中交换了p1和p2的值,当swap调用结束返回到main函数中,形参p1和p2存储空间释放,而main函数a和b的值始终未变。167.5.1 指针作为函数参数指针作为函数参数如果将swap函数写成能不能实现a和b交换呢?答案是不行。因为这样写的含义是:在swap函数中交换形参p1和p2的指针值,即交换后仅是p1和p2的指向发生了变化,而被指向的a和b的值始终未变。void swap(int *p1, int *p
8、2)int *t;t=p1, p1=p2, p2=t; /指针交换177.5.1 指针作为函数参数指针作为函数参数为了使被调函数能够改变主调函数的变量,应该用指针变量作为形参,将变量的指针(或地址)传递到被调函数中,通过指针间接引用达到修改变量的目的。当函数调用结束后,这些变量值的变化依然保留下来。换个角度看,这些变量带回了被调函数所做的修改,将运算结果返回到了主调函数中。187.5.1 指针作为函数参数指针作为函数参数显然,函数返回运算结果的前提有三个:使用指针变量作为函数形参;用接受运算结果的变量的指针(或地址)作为实参调用函数;函数中通过指针间接引用修改这些变量。197.5.1 指针作为
9、函数参数指针作为函数参数函数通过返回值只能返回一个运算结果,若要返回多个,就需要使用全局变量(因为全局变量对两个函数是可见的)。但全局变量使得函数模块化程度降低,现代程序设计思想要求尽量避免全局变量的使用。通过将指针作为函数参数的方法,既可以返回多个运算结果,又避免了使用全局变量。207.5.1 指针作为函数参数指针作为函数参数【例7.19】编写函数,计算并返回a和b的平方和、自然对数和、几何平均数、和的平方根。217.5.1 指针作为函数参数指针作为函数参数例7.191 #include 2 #include 3 double fun(double a, double b, double *
10、sqab, double *lnab,double *avg)4 5 *sqab=a*a+b*b; /*sqab返回平方和6 *lnab=log(a)+log(b); /*lnab返回自然对数和7 *avg=(a+b)/2; /*avg返回几何平均数8 return (sqrt(a+b); /函数返回和的平方根9 10 int main()11 12 double x=10,y=12,fsq,fln,favg,fsqr;13 fsqr=fun(x, y, &fsq, &fln, &favg);227.5.1 指针作为函数参数指针作为函数参数例7.1914 printf(%lf,%lf,%lf,
11、%lf,%lf,%lfn,x,y,fsq,fln,favg,fsqr);15 return 0;16 程序运行结果如下:10.000000,12.000000,244.000000,4.787492,11.000000,4.690416237.5.1 指针作为函数参数指针作为函数参数2数组作为函数形参在前面介绍过(一维或多维)数组作为函数的形参,例如:函数调用形式如下:double average(double A100, int n)./函数体double X100, f;f = average(X, 100);247.5.1 指针作为函数参数指针作为函数参数由于实参数组名X表示该数组的首地
12、址,因此形参应该是一个指针变量(只有指针变量才能存放地址),即函数定义等价于double average(double A100, int n)double average(double *A, int n)257.5.1 指针作为函数参数指针作为函数参数在函数调用开始,系统会建立一个指针变量A,用来存储从主调函数传来的实参数组首地址,则A指向数组X,其后可以通过指针访问数组元素。例如:Ai:指针下标法访问数组元素Xi;*(A+i):指针引用法访问数组元素Xi;&Ai、A+i:指向数组元素Xi。267.5.1 指针作为函数参数指针作为函数参数从应用的角度看,形参数组从实参数组那里得到了首地址,
13、因此形参数组与实参数组本质上是同一段内存单元。在被调函数中若修改了形参数组元素的值,也就是修改了实参数组元素。因此,用数组作为函数参数,也能够使函数返回多个运算结果。277.5.1 指针作为函数参数指针作为函数参数需要注意,形参数组“double A100”与数组定义写法一致,但含义不同。数组定义时A是数组名,是一个指针常量。而“double *A”是一个指针变量,在函数执行期间,它可以做赋值、自增自减运算等。例如:void fun(double *A, double B100)A+; /正确,A是指针变量B+; /错误,B是指针常量,不能做自增自减A=B; /正确,A是指针变量,可以重新指向
14、BB=A; /错误,B是指针常量,不能被赋值287.5.1 指针作为函数参数指针作为函数参数当函数调用开始时,形参指针变量的值是实参传来的地址,如果在函数中修改了形参指针变量,则实参传来的地址就会“丢失”,一般要将此地址保存下来。297.5.1 指针作为函数参数指针作为函数参数【例7.20】编写函数average,返回数组n个元素的平均值。307.5.1 指针作为函数参数指针作为函数参数例7.201 #include 2 double average(double *a, int n)3 /等价于average(double a, int n)4 double avg=0.0, *p=a;5
15、int i;6 for (i=1;i=n;i+,p+) avg=avg+*p; /等价于avg=avg+pi7 return n=0 ? 0 : avg/n ;8 9 int main()10 11 double x10=66,76.5,89,100,71.5,86,92,90.5,78,88;12 printf(average=%lfn,average(x,10);13 return 0;14 317.5.1 指针作为函数参数指针作为函数参数例7.201 #include 2 double average(double *a, int n)3 /等价于average(double a, in
16、t n)4 double avg=0.0, *p=a;5 int i;6 for (i=1;i=n;i+,p+) avg=avg+*p; /等价于avg=avg+pi7 returnn=0?0:avg/n;8 9 int main()10 11 double x10=66,76.5,89,100,71.5,86,92,90.5,78,88;12 printf(average=%lfn,average(x,10);13 return 0;14 average=83.750000程序运行屏幕327.5.1 指针作为函数参数指针作为函数参数综合前面指针变量和数组作为函数参数的结论,要想在函数中改变数
17、组元素,实参与形参的对应关系有如下4种作用相同的情况。337.5.1 指针作为函数参数指针作为函数参数(1)形参和实参都用数组名,例如:形参数组x接受了实参数组a的首地址,因此在函数调用期间,形参数组x与实参数组a是同一段内存单元。void fun(int x100, int n); /函数原型int a100;fun(a, 100); /函数调用347.5.1 指针作为函数参数指针作为函数参数(2)形参用指针变量,实参用数组名,例如:形参指针变量x指向实参数组a。void fun(int *x, int n); /函数原型int a100;fun(a, 100); /函数调用357.5.1
18、指针作为函数参数指针作为函数参数(3)形参与实参都用指针变量,例如:实参指针变量p的值传递到形参指针变量x中,则x也指向实参数组a。void fun(int *x, int n); /函数原型int a100, p=a;fun(p, 100); /函数调用367.5.1 指针作为函数参数指针作为函数参数(4)形参用数组,实参用指针变量,例如:形参数组x接受了实参指针变量传递进来的地址值,即数组a的首地址,因此可以理解形参数组x与实参数组a是同一段内存单元。void fun(int x100, int n); /函数原型int a100, p=a;fun(p, 100); /函数调用377.5.
19、1 指针作为函数参数指针作为函数参数上述4种情况,在函数fun中,均可以使用xi、*(x+i)、*x+等形式访问数组元素。387.5.1 指针作为函数参数指针作为函数参数无论形参是数组或是指针变量,在函数fun中无法检测到实参数组的实际长度。实际编程中,要么象本例一样将实际长度传递到函数内部,要么象字符串那样放一个结束标志,在函数中只要检测到结束标志,就结束数组元素往前访问,避免数组越界。397.5.1 指针作为函数参数指针作为函数参数特别地,如果实参不是一个数组,例如:在函数中如果把这个地址对应的内存单元当作数组来用,程序佷容易出现严重错误。void fun(int *x, int n); /函数原型int b, p=&b;fun(p, 100); /函数调用结束结束