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