C语言学习,这一篇就够了!(三)-- 函数
- 函数
函数是具有一定功能的一个模块,所谓函数名就是给该功能起了一个名字。
注意:函数就是功能。每一个函数用来实现一个特定的功能。函数的名字应反映出它代表的功能,这样代码的可读性会大大提升
记得在上一篇中有这么一句话,“一个C程序可由一个主函数和若干个其他函数构成。” C语言是一门完全面向过程的语言,在程序设计中要善于利用函数,以减少重复代码的编写,尽量的减少代码冗余,这样也能提高代码的可维护性,也更便于实现模块化的程序设计。
4.1 定义一个函数
通过了上面的解释,相信已经加深了对函数的理解以及对函数的作用有了进一步的认识。
定义一个函数需要包括以下几个内容:
- 指定函数的名字,以便以后的函数调用
- 指定函数的类型,即函数的返回值的类型
- 指定函数的参数的名字和类型,以便在调用函数的时候传递参数
- 书写函数的功能,这是函数的核心内容
4.1.1 定义一个无参函数
当函数不需要接受用户传递的数据时,不需要带参数
返回值类型 函数名() {
功能
}
下面我们来定义一个计算1加到100的函数
int sum()
{
int i, sum = 0;
for (i = 1; i <= 100; i++)
{
sum += i;
}
return sum;
}
通过将计算的结果保存到sum
中,最后通过return
语句返回给函数的调用者,返回值的类型是int
4.1.2 无返回值函数
有些函数不需要返回值,只需要执行代码即可,我们可以通过void
来指定返回值的类型
void hello() }{
printf("hello world");
}
这个函数没有返回值,它的功能就是输出hello world
,类型void
表示空类型或者无类型,大多数意味着无return语句
4.1.3 定义有参函数
当函数需要接收用户传递的数据,那么定义时就需要带上参数
int max(int x, int y) {
int z;
z = x > y ? x : y;
return z;
}
上面的函数功能是求两个数中较大的那个数。在主函数调用时,将数据传递给形参x,y,在函数体内判断两个书中较大的数,并通过return语句返回值返回给函数的调用者
注意:
- 参数的数据说明可以省略,默认值是
int
类型 - 函数名称需要遵循标识符命名规范
- 函数需要先定义后使用,如果函数写在了
main
函数后面,需要将函数的声明写到main
中函数调用之前 - 在C语言中不允许函数的嵌套定义
4.2 调用函数
调用函数的方式非常简单,以调用上面计算两数中最大值为例
c = max(a, b);
这样我们就能实现了函数的调用,将 a,b 传给max
函数,函数执行完毕后返回值z
的值赋值给c,这样c就得到了a,b中较大数的值
下面我们编写一个程序来练练手
输入两个整数,要求输出其中值较大者,使用函数来实现
首先我们先编写max函数,用来返回两个数中的较大者
int max(int x, int y) {
int z;
z = x > y ? x : y;
return z;
}
接下来我们编写主函数
int main()
{
int a, b, c;
printf("请输入两个数\n");
scanf("%d,%d", &a, &b);
c = max(a, b);
printf("max is %d", c);
return 0;
}
在主程序中接收两个用户输入的数据a,b,通过函数返回大的数,实现功能
注意:程序从上向下执行,当碰到函数名后,把值传给调用函数,当程序得到了返回值或调用函数结束,再继续往下执行。
4.3 形参和实参的区别
在上一部分中,我们复习了如何定义和调用函数。
如果函数是一个加工厂的话,那么函数的参数就是工厂的原材料,返回值就是经过加工的产品。
==重要==
-
形参只有在函数被调用时才会分配内存,调用结束后,会立刻释放内存,因此形参变量只有在函数内部有效,不能在函数外部使用。
-
实参和形参在数量上、类型上、顺序上必须严格一致,否则会发生“类型不匹配”的错误。如果会自动类型转换,或者进行了强制类型转换,那么实参类型也可以与形参类型不同。
-
函数调用中发生的数据传递是单向的,只能把实参的值传递给形参,而不能把形参的值反向地传递给实参。也就是改变形参不会影响实参的值。
注意:传数值,形参的变化不会改变实参的变化;传地址,形参的变化就有可能改变实参所对应的值
4.4 函数的嵌套调用
函数不能嵌套定义,但可以嵌套调用,也就是在一个函数的定义或调用过程中允许出现对另外一个函数的调用。这部分的内容过于简单就不过多阐述,就是在一个函数里调用另一个函数,无限套娃
4.5 函数的递归调用
在调用一个函数的过程中又出现直接或间接地调用该函数本身,称为递归调用,这也是C语言的特点之一。递归函数在解决许多数学问题上起了至关重要的作用,比如计算一个数的阶乘、生成斐波那契数列,等等。
注意:递归必须要有一个结束的条件,否则可能会无限的调用死循环
下面通过递归来输出斐波那契数列的第n项
#include <stdio.h>
int Fib(int n)
{
if (n == 0)
{
return 1;
}
else if (n == 1)
{
return 1;
}
else
return Fib(n - 1) + Fib(n - 2);
}
int main()
{
int n, ret;
printf("请输入n:");
scanf("%d", &n);
ret = Fib(n);
printf("%d\n", ret);
return 0;
}
重要语句return Fib(n - 1) + Fib(n - 2);
通过return再次调用这个函数,直至到达结束的条件。
4.6 全局变量和局部变量
4.6.1 局部变量
定义在函数内部的变量称为局部变量,它的作用域仅限于函数内部, 离开该函数后就是无效的,再使用就会报错。
int name(int a)
{
int b, c;
return a + b + c;
}
int main()
{
int x, y;
return 0;
}
- 在 main 函数中定义的变量也是局部变量,只能在函数中使用。main 函数也是一个函数,与其他函数平等地位
- 实参给形参传值的过程也就是给局部变量赋值的过程
- 可以在不同的函数中使用相同的变量名,它们表示不同的数据,分配不同的内存,互不干扰。(我偷偷的把它理解为js中的块级作用域)
4.6.2 全局变量
声明在函数外部的变量称为全局变量,它的作用域是整个作用域,也就是整个文件
4.6.3 练习题
输入长方体的长宽高求它的体积以及三个面的面积。(编写一个函数实现)
#include <stdio.h>
int s1, s2, s3; //面积
int volume(int a, int b, int c)
{
int v; //体积
v = a * b * c;
s1 = a * b;
s2 = b * c;
s3 = a * c;
return v;
}
int main()
{
int v, length, width, height;
printf("输入长宽高: ");
scanf("%d %d %d", &length, &width, &height);
v = volume(length, width, height);
printf("v=%d, s1=%d, s2=%d, s3=%d\n", v, s1, s2, s3);
return 0;
}
采用了三个全局变量,来记录三个面的面积,这样通过main函数可以直接通过访问全局变量来获取到对应面积的值,通过返回值来得到体积v
注意:建议在不必要的情况下不要使用全局变量(这个在其他语言中也是同样的)
原因:
- 全局变量在程序的全部执行过程中都要占用存储单元,而不是仅在需要时才开辟单元
- 它使函数的通用性降低了,不利于模块化编程
- 降低了代码的可读性
4.7 内部函数和外部函数
- 不能被其他源文件调用的函数称谓内部函数 ,内部函数由
static
关键字来定义,因此也叫静态函数,如static int max()
- 能被其他源文件调用的函数称谓外部函数 ,外部函数由extern关键字来定义,形式为
extern int max()
- 在没有指定函数的作用范围时,系统会默认为外部函数
// hello.c文件
void hello()
{
printf("hello world");
}
//main.c文件
#include<stdio.h>
#include "hello.c"
int main() {
hello();
return 0;
}
4.8 练习题
第一题
在 C 语言中,有关函数的说法,以下正确的是 。
A. 函数可嵌套定义,也可嵌套调用 B. 函数可嵌套定义,但不可嵌套调用
C. 函数不可嵌套定义,但可嵌套调用 D. 函数不可嵌套定义,也不可嵌套调用
==答案:C==
第二题
有一个函数原型如下所示,则该函数的返回类型为( ) 。
abc(float x,float y)
A. void B. double C. int D. float
==答案:C== 默认int
第三题
C语言中,以下叙述中错误的是( )。
A) 主函数中定义的变量是全局变量
B) 同一程序中,全局变量和局部变量可以同名
C) 全局变量的作用域从定义处开始到本源程序文件结束
D) 局部变量的作用域被限定在其所定义的局部范围中
==答案:A== 函数同级
- 点赞
- 收藏
- 关注作者
评论(0)