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