设计函数一定要明确函数的返回值,只有明确返回值才能确定函数的类型。函数返回值和参数是两回事。参数是指函数参数表中的变量,这个变量视传递方式而变。传数值不改变参数的值,而传地址值是改变参数值的必要条件,但是否改变,则视使用方法而定。函数返回值是指被调用函数结束时所返回的值,非void类型的返回值就是return后面表达式的值。返回值的设计就是要保证调用它的函数正确接收这个值。
6.7.1 无返回值的void类型函数
虽然void类型函数不返回值,但并不说明它不能提供中间值。
【例6.9】找出下面程序中的错误。
#include <stdio.h> void max (int , int ); // 函数参数采用传数值方式 int main () { int a , b ,c ; printf (\" 输入两个整数:\" ); scanf (\"%d %d\" ,&a ,&b ); c=max (a ,b ); return 0 ; } // 将变量作为参数,以传数值方式传递参数 void max (int a , int b ) { if (a<b ) printf (\" 最大值是:%dn\" ,b ); else printf (\" 最大值是:%dn\" ,a ); }
【解答】max没有返回值,它直接输出求值结果,不能将它赋值给c。删除变量c的声明,然后直接使用语句“max(a,b);”即可。这种调用方式称为直接调用函数语句。
【例6.10】找出下面程序中的错误。
#include <stdio.h> void max (int , int ); // 函数参数采用传数值方式 int main () { int a , b ,c ; printf (\" 输入两个整数:\" ); scanf (\"%d %d\" ,&a ,&b ); max (a , b ); // 传数值 printf (\" 最大值是:%dn\" ,c ); return 0 ; } // 将变量作为参数,以传数值方式传递参数 void max (int a , int b ) { if (a<b ) c=b ; else c=a ; }
【解答】max函数使用的是主函数里的变量c,这是错误的。在主函数之外将c声明为全局变量即可。这时虽然也是函数语句调用,但被调函数改变全局外部变量的值。一定要注意:void函数没有返回值,但它可以改变外部变量的值。虽然一般不提倡使用这种方法,但在某种场合下还是很有用的。因为它简化了函数本身的设计,如果已经存在外部变量,为何不好好利用呢?函数可以使用外部变量,所以可直接将结果赋给外部变量。主函数就可以使用外部变量的值。
注意外部变量名不能与函数的变量名相同。在上例中,如果声明全局变量c,而没有去掉主程序的变量c,就会出错。
【例6.11】为何下面的程序计算的结果不对?
#include <stdio.h> void max (int , int ,int ); int main () { int a , b ,c ; printf (\" 输入两个整数:\" ); scanf (\"%d %d\" ,&a ,&b ); max (a , b ,c ); c=a+b+c ; printf (\"c=%dn\" ,c ); return 0 ; } void max (int a , int b ,int p ) { if (a<b ) p=b ; else p=a ; }
【解答】max的第3个参数要设为指针参数。修改后的程序如下:
#include <stdio.h> void max (int , int ,int* ); int main () { int a , b ,c ; printf (\" 输入两个整数:\" ); scanf (\"%d %d\" ,&a ,&b ); max (a , b ,&c ); c=a+b+c ; printf (\"c=%dn\" ,c ); return 0 ; } void max (int a , int b ,int * p ) { if (a<b ) *p=b ; else *p=a ; }
【例6.12】下面程序在函数原型的声明中,对数组采用两种不同的声明,哪个声明是正确的?所设计的函数类型是无返回值的void类型,程序想对数组a的元素数值反序,设计为void类型能行吗?
#include<stdio.h> // 预编译命令 void Exch (int * ); void Display (int [ ] ); int main () { int a={1 ,3 ,5 ,7 ,9} ; Display (a ); Exch (a ); Display (a ); return 0 ; } void Exch (int a[ ] ) { int c ; c=a[0] ; a[0]=a[4] ; a[4]=c ; c=a[1] ; a[1]=a[3] ; a[3]=c ; } void Display (int a[ ] ) { int i ; for ( i=0 ;i<5 ;i++ ) printf (\"%d \" , a[i] ); printf (\"n\" ); }
【解答】对数组而言,数组名就是首地址的指针,所以两个格式都是可以的。
void类型的函数是说函数没有返回值,并不是说void类型的函数不能改变传递的参数值。函数的返回值不能是数组,但可以将数组作为参数以传地址值的方式传给被调函数,由被调函数通过存储数组的地址修改数组元素的值。
程序运行结果如下:
1 3 5 7 9 9 7 5 3 1
6.7.2 函数返回值问题
1.非void类型的函数必须返回一个值
【例6.13】下面的程序用来改变字符数组的内容。
#include <string.h> #include <stdio.h> int st ( char ); int main ( ) { char s=\"Good Afternoon !\" ; printf (\"%sn\" , s ); st (s ); printf (\"%sn\" , s ); return 0 ; } int st (char s ) { strcpy (s ,\"How are you ?\" ); // 改变字符数组内容 }
第1次编译给出警告信息,第2次能产生正确结果。如何排除警告信息?
【解答】st函数没有返回值。因为主函数没有使用变量接收函数的返回值,而函数直接修改参数的值,所以只给出警告信息。在st函数增加“return 0;”语句即可排除警告信息。
对于非void类型,即使不使用它的返回值,也必须使用return语句返回一个值。一般返回0值表示正常返回。
其实,本程序不需要返回值,将st函数设计为void类型,即
void st (char s ) { strcpy (s ,\"How are you ?\" );}
2.函数使用临时变量作为返回值
【例6.14】试问这个程序正确吗?
#include <stdio.h> int max (int , int ); int main () { int a=33 ,b=55 ,c=0 ,d=100 ; max (a , b ); //6 d=d+c ; //7 printf (\"c=%dn\" ,d ); return 0 ; } int max (int a , int b ) { int x=5 ,y=8 ,c ; if (a<b ) c=b+x ; else c=a+y ; return c ; }
【解答】程序运行正确,但没有意义。程序没有实现任何功能。max使用自己的临时变量c作为返回值,这个变量与主程序的同名变量没有关系。主程序没有接收这个返回值,也没有使用它,max调用结束,返回值也就失去任何意义。
主程序使用它只能有两种方式。一是使用一个同类型的变量接收它的返回值。修改语句
c=max (a ,b );
这时“d=d+c;”就有了意义。二是作为printf函数的参数,删除6和7两条语句,使用
printf (\"c=%dn\" ,d+max (a ,b ));
输出最终结果。
3.不能使用临时数组名作为返回值
【例6.15】返回值错误的例子。
#include <stdio.h> int *sp ( int [ ] ); int main ( ) { int a[3]={1 ,3 ,5} ,i ,*p ; for (i=0 ; i<3 ; i++ ) printf (\"%d \" , a[i] ); printf (\"n\" ); p=sp (a ); for (i=0 ; i<3 ; i++ ) printf (\"%d \" , * (p+i )); printf (\"n\" ); return 0 ; } int *sp (int s ) { int b[3] ; b[0]=2+s[0] ; b[1]=4+s[1] ; b[2]=6-s[2]+b[1] ; return b ; }
数组b是函数sp的临时数组,函数不能返回数组,这里其实是返回数组首地址的指针。虽然使用指针把存储b的首地址返给主函数,但当函数消失后,数组也就不存在了。所以返回的只是一个指向原来存储b的首地址,因为数组b不存在了,当然这个地址的内容也就不可预测了,所以对这个地址的一系列操作,也就无所适从了。
解决的办法是使用static定义数组b,使得返回的指针指向静态数组b。由此可见,并不是使用指针就能保证返回值。被调函数里定义的变量可以作为返回值,但不能使用普通数组。
4.返回临时指针必须是首地址
【例6.16】下面是将例6.15的sp函数改写的程序,找出存在的错误。
int *sp (int s ) { int *p ; p= (int* )(malloc (3*sizeof (s ))); *p++=2+s[0] ; *p++=4+s[1] ; *p=6-s[2]+* (p-1 ); p=p-1 ; return p ; }
【解答】程序计算错误。*(p-1)是利用偏移量,没有移动指针的位置,这时的指针是指向第3个元素。返回指针时,一定要保证是分配给指针的首地址。
将“p=p-1;”改为:“p=p-2”即可。
另外,申请内存的数量是sizeof(s)(它计算的是整个数组)。推荐直接使用下标,即
int *sp (int s ) { int *p ; p= (int* )(malloc (sizeof (s ))); p[0]=2+s[0] ; p[1]=4+s[1] ; p[2]=6-s[2]+p[1] ; return p ; }
每种数据类型都可定义相应的函数类型和指针函数,并在函数里面使用return语句返回一个或多个返回值,但每次调用只有一个满足返回条件,而且返回值的类型必须与函数类型一致。除非出错时的强行退出,否则均绝无例外。
void不能定义数据类型,但可以定义函数和指针。由于void类型的函数没有返回值,所以常用来输出信息。
如果不允许被调函数修改实参的值,可以使用const限定。