跳轉到內容

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" 語句只能返回一個值,但該值可以是指向陣列或資料結構的 "指標"。指標是一個複雜的概念,將在後面詳細討論。

華夏公益教科書