名著阅读 > C语言解惑 > 6.7 函数的返回值 >

6.7 函数的返回值

设计函数一定要明确函数的返回值,只有明确返回值才能确定函数的类型。函数返回值和参数是两回事。参数是指函数参数表中的变量,这个变量视传递方式而变。传数值不改变参数的值,而传地址值是改变参数值的必要条件,但是否改变,则视使用方法而定。函数返回值是指被调用函数结束时所返回的值,非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限定。