当程序功能比较多,规模比较大时,会使得主函数变得庞杂、冗长,难以阅读、维护,并且功能代码不能复用。为了解决这种问题,C 语言引入了模块化程序设计的思想,类似于搭积木,事先将各个功能代码拆分为单独的程序模块,需要时,直接在 main 函数中进行“组装”即可。
函数,就是实现特定功能的程序模块,是 C 程序实现模块化思想的关键。在 C 语言中,函数分为以下 2 种:
(1)库函数。由系统、或第三方库提供,直接调用即可,无需关心函数内部实现,例如:printf、getchar……等函数。
(2)用户自定义函数。由程序员自己手动封装,需要了解函数内部实现。
如果系统库已经提供了实现某个功能的函数,直接调用系统库函数即可,否则需要用户自己编写代码实现。
函数由“名字、参数、返回值”组成,名字是调用函数的时候通过哪个名字,参数指的是传递什么数据,返回值是执行的结果。
“调用发射火箭的指令,1 号火箭放到 3 号发射架上,发射结果【成功】”:int i=fire(1,3);。函数可以没有参数,也可以没有返回值。
(一) 函数基础
1. 定义函数
在 C 语言中,函数既可以有参数,也可以没有参数。
定义无参函数的一般形式为:
类型名 函数名()
{
语句;
}
或
类型名 函数名(void)
{
语句;
}
其中,类型名用来指定函数返回值类型。如果函数没有返回值,可以将类型名写为 void。函数名后面小括号中,如果没有参数,可以不写或写为 void,表示调用该函数时无需传入数据,例如:getchar 函数。
无参函数:
void show(void) //无参函数 show
{ //{ 函数开始标志
printf("hello world"); //函数体
} //} 函数结束标志
定义有参函数的一般形式为:
类型名 函数名(形参列表)
{
语句;
}
有参函数与无参函数唯一的不同,就是函数名后面多了参数列表,表示调用该函数时需要传入数据,例如:printf 函数。
有参函数:
void show(int num) //有参函数 show
{
printf("num=%d
",num);
}
【注意】
函数名后面小括号中的参数,本质上就是局部变量,在该函数被调用时,由外部为该参数变量进行赋值。
2. 调用函数
定义函数的目的就是为了调用函数,函数类型不同,调用方式也不相同。函数调用的一般形式为:
函数名 (参数列表);
如果要调用的函数没有参数,则不用传递参数列表;如果有参数,就要按照顺序和类型传递参数,参数之间用逗号分隔。
下面通过例子来了解函数调用。
#include<stdio.h>
void show() //定义 show 函数
{
printf("hello world"); //输出“hello world”
}
int main(void)
{
show(); //调用 show 函数
getchar();
return 0;
}
调用有参函数:
#include<stdio.h>
void show(int num) //定义 show 函数,形参为 num
{
printf("num=%d",num); //输出形参 num 数值
}
int main(void)
{
show(100); //调用 show 函数,传入 100
getchar();
return 0;
}
3. 实参与形参
提到参数的时候有“形参”、“实参”两个概念,全程是“形式参数”、“实际参数”。
函数形参,就是函数定义时函数名后面小括号中的变量。需要注意的是,形参只能是变量。
函数实参,就是调用函数时传入的数据。实参可以是字面量、变量、表达式……等。
通俗理解:形参是定义函数时候用的;实参是调用函数时候“实际”传递的数据。
#include<stdio.h>
void show(int num) //num 是形参
{
printf("num=%d
",num);
}
int main(void)
{
int a=5;
show(100); //100 是实参
show(a);
show(a+3);
getchar();
return 0;
}
4. 实参、形参的误解
在 C 程序中,函数实参无论是字面量、变量、表达式……最终都会将结果值赋给形参变量,赋值的顺序只与形参的顺序有关,与变量名无关。
示例代码如下:
#include<stdio.h>
void show(int a,int b) //形参 a、b
{
printf("a=%d b=%d
",a,b);
}
int main(void)
{
int a=10,b=20;
show(b,a); //实参 a、b
getchar();
return 0;
}
//运行结果
//a=20
//b=10
上述程序中,由于实参变量与形参变量名称相同,有的人误以为函数传参是根据变量名进行匹配,就会认为实参 a 赋值给形参 a、实参 b 赋值给形参 b,其实这是一种错误理解。
在 C 语言中,实参、形参赋值只与参数顺序有关,与参数名无关:第 1 个实参赋给第 1 个形参、第 2 个实参赋给第 2 个形参、依次类推……第 n 个实参赋给第 n 个形参。
5. 注意事项
(1)同一个.c 文件中,函数名之间不能重名。
(2)用户自定义函数一般需要放置在调用它的函数之前,如果放置在调用它的函数之后,就需要添加函数声明,把函数的声明放到调用之前,函数的实现可以写到后面。
void f2(int i1,int i2);//函数的声明、函数的定义
int main()
{
f2(i2,i1);
getchar();
return 0;
}
void f2(int i1,int i2)//函数的实现
{
printf("f2,i1=%d,i2=%d
",i1,i2);
}
项目比较大的时候,一般统一把所有自定义函数的声明写到 main 之前,实现写到 main之后。
(3)调用函数的时候参数的个数和类型要匹配。
(4)不能在函数中定义函数。
6. 局部变量
前面讲过定义在函数内部的变量被称为局部变量。局部变量只能在当前函数中访问,不能跨函数访问。
//【错误的代码】
#include<stdio.h>
void show()
{
int a=10; //局部变量 a
}
int main(void)
{
show();
printf("%d",a); //出错代码
getchar();
return 0;
}
(1) 函数内部的局部变量不能被其他参数操作;
(2) 函数的形参在函数内部可以当成普通变量看待,还是局部变量。
(3) 普通参数,进行函数调用的时候传递的是数据的“副本”,在函数内部对形参的赋值,不会影响外部的实参变量
void test(int i)//函数的形参在函数内部可以当成普通变量看待,还是局部变量
{
printf("test 开始%d
",i);
i=9;//在函数内部对形参的赋值,不会影响外部的实参变量
printf("test 最后%d
",i);
}
int main()
{
int a=5;
test(a);//普通参数,进行函数调用的时候传递的是数据的“副本”
printf("%d
",a);
getchar();
return 0;
}
7. 函数返回值
按照有无返回值,函数可以分为无返回值函数和有返回值函数。对于无返回值函数的返回值类型为 void。对于有返回值函数,需要显示指定返回值类型,这类函数的返回值通过return 语句得到。
return 是 C 语言中的关键字,后面可以跟字面量、变量、表达式……等。例如:
return 100; //返回字面量 100
return a; //返回变量 a 的值
return a+b; //返回表达式 a+b 的运行结果
return 语句的作用主要有 2 个:
(1)当函数执行到 return 语句时,会将 return 后面的结果值返回给主调函数,也就是说,在主函数中可以得到被调函数中的数据。
(2)return 语句被执行时,含中 return 下面的语句将不再执行。因此,return 语句可以看作是函数执行结束的标志。
函数返回值类型指的是函数返回数据的类型。例如:
char getChar();
int getInt();
#include<stdio.h>
int getInt()
{
int a=10;
return a; //返回变量值
}
int main(void)
{
int result=0;
result=getInt(); //接收返回值
printf("result=%d",result);
getchar();
return 0;
}
return 会终止函数的执行。
#include<stdio.h>
int getInt() //定义 getInt 函数
{
printf("return 前
");
return 10; //执行 return 语句
printf("return 后
");
}
int main(void)
{
int result=0;
result=getInt(); //调用 getInt 函数
printf("result=%d",result);
getchar();
return 0;
}
(二) 函数深入
1. 函数参数的传递方式:值传递
在 C 语言中,实参向形参传递数据方式是“值传递”。并且是单向的,只能实参向形参赋值,反之不行。当函数被调用时,系统会为形参变量分配内存空间,然后将实参的值复制到形参对应的内存空间中。因此,实参与形参对应的内存空间是不同的,但是内存空间中的数据是相同,形参相当于实参的一个副本。
由于形参只是实参的一个副本,那么修改实参将不会影响形参。
#include<stdio.h>
void show(int num)
{
num++; //形参自加 1
printf("形参 num=%d
",num); //输出形参
}
int main(void)
{
int num=20;
show(num); //调用 show 函数,传入实参 num
printf("实参 num=%d",num); //输出实参
getchar();
return 0;
}
2. 无返回值函数中的 return 语句
return 语句使用在无返回值函数中,后面直接加分号即可。表示只终止函数执行,不返回数据。
#include<stdio.h>
void show()
{
printf("return 前
");
return; //执行 return 语句,终止函数执行
printf("return 后
");
}
nt main(void)
{
show(); //调用 show 函数
getchar();
return 0;
}
3. 函数调用的其他形式
在 C 程序中,根据函数调用出现位置的不同,可以将其分为 3 种:
第 1 种 函数调用语句。
函数调用后面加上英文分号就是函数调用语句,大多数情况下用于无返回值函数,有时也会用于有返回值函数。例如:
show();
printf(“hello world”);
show 是用户自定义函数,实现向屏幕打印“hello world”的功能,该函数无返回值。
printf 是系统库函数,也实现了向屏幕打印“hello world”的功能,但是该函数是有返回值的,不过程序中一般使用不到。
第 2 种 函数调用表达式。
函数调用作为表达式的其中一项,出现表达式中。例如:
result=getInt();
result=100+getInt();
上面第 1 行语句,先调用 getInt 函数,然后将函数返回值赋值给变量 result。
第 2 行语句,先调用 getInt 函数,然后将函数返回值和 100 做加法运算,最后将运算结果赋值给变量 result。
当函数调用作为表达式时,要求该函数必须有返回值,也就是说必须是有返回值函数。