C语言——函数(2)

函数的递归调用

定义:

在调用一个函数的过程中又出现直接或间接地调用该函数本身,称为函数的递归调用。C语言的特点之一就在于允许函数的递归调用。

直接递归:执行函数时调用函数本身。

eg:

a()-->a()

间接递归:通过别的函数调用函数本身。

eg:

a()-->b()-->a()

本质:

是一种循环结构,它不同于之前所学的while,do-while,for这样的循环结构,这些循环结构是借助循环变量,而递归是利用函数自身实现循环结构,如果不加以控制,很容易产生死循环。

递归调用的注意事项:
1.递归调用必须要有出口,一定要终止递归(否则会产生死循环)。
2.对终止条件的判断一点要放在函数递归之前。

3.进行函数的递归调用。

4.函数递归的同时一定要将函数调用向出口逼近。

eg:

1/**
2 * 需求:递归案例-求阶乘(n!)
3 */
4#include <stdio.h>
5
6/* 编写一个函数,用来求阶乘 */
7long fac(int n)
8{
9    // 因为int型表示的数据范围小,所以乘法操作我们使用long来接收计算结果
10    long f;
11    
12    if(n < 0)
13    {
14        printf("n的范围不能是0以下的数!\n");
15    }
16    else if(n == 0 || n==1) // 此时不满足阶乘条件
17    {
18        f = 1; 
19    }
20    else
21    {
22        f = fac(n-1)*n;  
23    }
24    
25    return f;
26}
27
28int main()
29{
30    int n;
31    printf("请输入一个整数:\n");
32    scanf("%d",&n);
33    printf("%d!=%ld\n",n,fac(n));
34
35    return 0;
36}
37

数组做函数参数

注意:

        当用数组做函数的实际参数时,则形参应该也要用数组/指针变量来接收,但请注意,此次并不代表传递了数组中所有的元素数据,而是传递了第一个元素的内存地址(数组首地址),形参接收这个地址后,则形参和实参就代表了同一块内存空间,则形参的数据修改会改变实参的。这种数据传递方式我们可以称之为"引用传递"。

        如果用数组做函数形式参数,那么我们提供另一个形参表示数组的元素个数。原因是数组形参代表的仅仅是实际数组的首地址。也就是说形参只获取到了实际数组元素的开始,并未获取元素的结束。所以提供另一个形参表示数组的元素个数,可以防止在被调函数对实际数组元素访问的越界。

        但有一个例外,如果是用字符数组做形参,且实际数组中存放的是字符串数据(形参是字符数组,实参是字符串)。则不用表示数组元素的个数的形参,原因是字符串本身会自动结束符\0。

eg:

1/**
2 * 需求:数组函数的参数案例-编写一个函数,用来分别求数组score_1(有5个元素)和数组score_2(有         
  10个元素)各元素的平均值 。
3 */
4#include <stdio.h>
5
6/* 定义一个函数,用来求平均分 */
7float avg(float scores[],int len)
8{
9    int i;// 循环变量
10    float aver,sum = scores[0];// 保存平均分和总成绩
11
12// 遍历集合
13    for(i = 1;i < len;i++)
14    {
15        sum += scores[i];
16    }
17    
18    aver = sum / len;
19
20    return aver;
21}
22
23int main()
24{
25    //准备俩测试数组
26    float score_1[5] = {66,34,46,37,97};
27    float score_2[10] = {77,88,66,55,65,76,87,98,75,34};
28
29    printf("这个班的平均分是:%6.2f\n",avg(score_1,sizeof(score_1)/sizeof(float)));
30    printf("这个班的平均分是:%6.2f\n",avg(score_2,sizeof(score_2)/sizeof(float)));
31
32    return 0;
33}
34

变量的作用域

引入问题:

我们在函数设计过程中,经常要考虑对参数的设计,换句话说,我们需要考虑函数需要几个参数,需要什么类型的参数,但我并没有考虑函数是否需要提供参数,如果说函数可以访问到已定义的数据,则就不需要提供函数形参,那么我么到底要不要提供函数参数,取决于什么?答案就是变量的作用域(如果函数在变量的作用域范用内,则函数可以直接访问数据)。

变量的作用域

概念:变量的作用范围,也就是说变量在什么范围是有效的。

变量的分类

根据变量的作用域,变量可分为全局变量和局部变量

局部变量

序号局部变量作用域
1形式参数(形参)函数作用域
2函数内定义的变量函数作用域
3复合语句中定义的变量块作用域
4for循环表达式1定义的变量块作用域

全局变量

序号全局变量作用域
1定义在函数之外的变量,也称为外部变量或全程变量从全局变量定义处到本源文件(.c)的结束

建议在全局变量定义时初始化。如果不初始化,系统会将全局变量初始化为0(0|\0|0.0)

使用全局变量的优缺点:

优点:

1.利用全局变量可以实现一个函数对外输出的多个结果数据。

2.利用全局变量可以减少的数形参个数,从而降低内存消耗,以及因形参传递带来的时间消耗

缺点:

1.全局变量在程序的整个运行期间,始终占据内存空间,会引起资源消耗。

2.过多的全局变量会引起程序的混乱,造成程序结果错误。

3.降低程序通用性,特别是当我们进行函数移植时,不仅仅要移植函数,还要考虑全局变量。

4.违反了“高内聚,低耦合”的程序设计原则。

总结:

弊大于利,建议尽量减少对全局变量的使用,函数之间要产生联系,仅通过实参-形参的方式产生联系。

注意:

如果全局变量(外部变量)和局部变量同名,程序执行的时候,就近原则

变量的生存周期

概念:

变量在程序运行中的存在时间。

根据变量存在的时间不同,变量可分为静态存储方式(长)和动态存储方式(短)。

变量的存储类型

变量的完整定义格式:[存储类型] 数据类型 变量列表;

存储类型

auto

auto存储类型只能修饰局部变量,被auto修饰的局部变量是存储在动态存储区的。auto也是局部变量默认的存储类型。

int a = 10; 等价于 auto int a = 10;

static

修饰局部变量:局部变量会被存储在静态存储区。局部变量的生命周期被延长,但是作用域不发生改变。

修改全局变量:全局变量的生命周期不变,但作用域被衰减。一般限制全局变量只能在本文件内。

demo01.c

#include "demo01.h"

// 全局变量

static int fun_a = 10;

int fun1()

demo02.c

#include "demo01.h"

main()

{

 // 此时fun_a就不能被其他文件访问

fun_a = 20;}

extern

外部存储类型:只能修饰全局变量,ci全局变量可以被其他文件访问。相当于扩展了全局变量的作用域。

extern修饰外部变量,往往是外部变量进行声明,声明该变量是在外部文件中定义的;不是变量定义。

demo01.c

#include "demo01.h"

int fun_a = 10;

int fun1(){..}

demo02.c

#include "demo01.h"

// 声明外部文件的变量

extern int fun_a;

// 声明外部文件的函数

extern int fun1();

main()

{

 fun_a = 20;

fun1();

}

register

寄存器存储类型:只能修饰局部变量,用register修饰的局部变量会直接存储到CPU的寄存器中,往往将循环变量设置为寄存器存储类型。

面试题

static关键字的作用

1. static修饰局部变量,延长其生命周期,但不影响局部变量的作用域。

2. static修饰全局变量,不影响全局变量的生命周期,会限制全局变量的作用域仅限本文件内使用;

3. static修饰函数:此函数就称为内部函数,仅限本文件内调用。static int funa(){..}

内部函数和外部函数

内部函数:使用static修饰的函数,称作内部函数,内部函数只能在当前文件中调用。

外部函数:使用extern修饰的函数,称作外部函数,extern是默认的,可以不写,也就是说本质上我们所写的函数都是外部函数,建议外部函数在被其他文件调用的时候,在其他文件中声明的时候,加上extern关键字。

值传递与引用传递

值传递:

发生在整型、浮点型、字符型,数据传递,传递的是数值,也就是内存空同只能被当前变量独享

引用传递:

发生在数组、指针、结构体.,数据传递,传递的是地址值,也就是内存空间可以被多个变量共享

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值