跳转到内容

C 语言入门/C 函数详解

来自维基教科书,自由的教科书

如前所述,任何 C 程序都必须包含一个 "main()" 函数,其中包含程序运行时默认执行的代码。

一个程序可以包含任意数量的函数。所有函数对其他所有函数都是 "可见" 的。例如

   /* fdomain.c */

   #include <stdio.h>;

   void func1( void );
   void func2( void );
   
   int main()
   {
     puts( "MAIN" );
     func1();
     func2();
   }

   void func1( void )
   {
     puts( "FUNC1" );
     func2();
   }

   void func2( void )
   {
     puts( "FUNC2" );
     func1();
   }

在这个例子中,"main()" 可以调用 "func1()" 和 "func2()";"func1()" 可以调用 "func2()";"func2()" 可以调用 "func1()"。原则上,即使

"main()" 也可以被其他函数调用,但很难弄清楚为什么要这样做。虽然 "main()" 是上面列表中的第一个函数,但没有特别要求它必须如此,但按照惯例,它始终应该如此。

函数可以递归地调用自身。例如,"func1()" 可以无限地调用 "func1()",或者至少在发生堆栈溢出之前。您不能在其他函数内声明函数。

函数定义如下

   float sphere( int rad )
   { 
      ...
   }

它们以函数头开始,函数头以返回值类型声明(本例中为 "float")开头,然后是函数名(本例中为 "sphere"),最后是所需的实参(本例中为 "int rad")。

ANSI C 规定提供函数原型,以允许编译器对函数调用执行更好的检查

   float sphere( int rad );

例如,考虑一个简单的程序,它 "发射" 一种武器(只需打印 "BANG!")。

   /* bango.c */

   #include <stdio.h>

   void fire( void );

   void main()
   {
     printf( "Firing!\n" );
     fire();
     printf( "Fired!\n" );
   }

   void fire( void )
   {
     printf( "BANG!\n" );
   }

这将打印

   Firing!
   BANG!
   Fired!

由于 "fire()" 不返回值,也不接受任何实参,因此返回值和实参都被声明为 "void";"fire()" 也不使用 "return" 语句,在完成时自动返回。

让我们修改此示例,以允许 "fire()" 接受一个实参来定义射击次数。这将为程序提供

   /* fire.c */

   #include <stdio.h>

   void fire( int n );

   void main()
   {
     printf( "Firing!\n" );
     fire( 5 );
     printf( "Fired!\n" );
   }

   void fire( int n )
   {
     int i;
     for ( i = 1; i <= n ; ++i )
     {
       printf( "BANG!\n" );
     }
   }

这将打印

   Firing!
   BANG!
   BANG!
   BANG!
   BANG!
   BANG!
   Fired!

此程序将一个参数(一个整数)传递给 "fire()" 函数。该函数使用 "for" 循环执行指定次数的 "BANG!" - 关于 "for" 的更多信息将在后面介绍。

如果函数需要多个实参,可以使用逗号将它们隔开

   printf( "%d times %d = %d\n", a, b, a * b );

"参数" 一词有时会用来代替 "实参"。这两个术语之间实际上有一个细微的差别:调用例程为被调用函数指定 "实参",而被调用函数从调用例程接收 "参数"。

当参数在函数头中列出时,它将成为该函数的局部变量。它被初始化为调用例程提供的实参值。如果变量用作实参,则不需要与函数头中指定的参数具有相同的名称。

例如

  fire( shots );
  ...
  void fire( int n )
  ...

传递给 "fire()" 的整数变量名为 "shots",但 "fire()" 在名为 "n" 的局部变量中接受 "shots" 的值。实参和参数也可以具有相同的名称,但即使这样,它们仍然是不同的变量。

参数当然按照它们发送的顺序与实参匹配

   /* pmmatch.c */

   #include <stdio.h>

   void showme( int a, int b );

   void main()
   {
     int x = 1, y = 100;
     showme( x, y );
   }

   void showme( int a, int b )
   {
     printf( "a=%d  b=%d\n", a, b );
   }

这将打印

   a=1  b=100

此程序可以修改为显示实参不受函数对参数执行的任何操作的影响,如下所示

   /* noside.c */

   #include <stdio.h>

   void showmore( int a, int b );

   void main()
   {
      int x = 1, y = 100;
      showmore( x, y );
      printf( "x=%d  y=%d\n", x, y );
   }

   void showmore( int a, int b )
   {
      printf( "a=%d  b=%d\n", a, b );
      a = 42;
      b = 666;
      printf( "a=%d  b=%d\n", a, b );
   }

这将打印

   a=1  b=100
   a=42  b=666
   x=1  y=100

数组可以像任何其他类型的变量一样发送到函数

   /* fnarray.c */

   #include <stdio.h>
   #define SIZE 10
   
   void testfunc( int a[] );
   
   void main()
   {
     int ctr, a[SIZE];
     for( ctr = 0; ctr < SIZE; ++ctr )
     {
       a[ctr] = ctr * ctr;
     }
     testfunc( a );
   }
   
   void testfunc( int a[] )
   {
     int n;
     for( n = 0; n < SIZE; ++ n )
     {
       printf( "%d\n", a[n] );
     }
   }

可以定义具有可变数量参数的函数。实际上,"printf()" 就是这样的函数。这是一个比较高级的问题,我们在这篇文档中不会再深入讨论它。

从函数中获取值的常规方法是将其作为返回值提供。这很好地封装了函数,并将其与调用例程隔离。在第一部分的示例中,"sphere()" 函数使用以下语句返回了一个 "float" 值

   return( result );

调用例程如下接受返回值

   volume = sphere( radius );

返回值可以直接用作其他函数的参数

   printf( "Volume: %f\n", sphere( radius ) );

返回值不必使用;例如,"printf()" 返回它打印的字符数,但很少有程序会费心检查。

一个函数可以包含多个 "return" 语句

   if( error == 0 )
   {
     return( 0 );
   }
   else
   {
     return( 1 );
   }

"return" 可以放置在函数中的任何位置。它不必返回值;如果没有值,"return" 只是导致退出函数。但是,这确实意味着函数的数据类型必须声明为 "void"

   void ftest( int somevar )
   {
      ...
      if( error == 0 )
      {
        return();
      }
      ...
   }

如果函数中没有 "return",则函数在执行完最后一个语句后返回。同样,这意味着函数类型必须声明为 "void"。

"return" 语句只能返回一个值,但该值可以是指向数组或数据结构的 "指针"。指针是一个复杂的概念,将在后面详细讨论。

华夏公益教科书