C语言函数(二)

0 阅读5分钟

 一、嵌套调用和链式访问

1.1嵌套调用

        嵌套调用简单来说就是在你写的函数中调用另一个函数。比如我们要计算某年某月有多少天,我们写两个函数来实现。 

• is_leap_year():根据年份确定是否是闰年
• get_days_of_month():调⽤is_leap_year确定是否是闰年后,再根据月计算这个月的天数

这一段代码,完成了⼀个独立的功能。代码中反应了不少的函数调用:
• main 函数调用 scanf 、 printf 、 get_days_of_month。
• get_days_of_month 函数调用 is_leap_year。

1.2链式访问

        所谓链式访问就是将一个函数的返回值作为另外一个函数的参数,像链条一样将函数串起来就是函数的链式访问。

例如:

将strlen的返回值直接作为printf函数的参数,这就是一个链式访问。

我们再看一个有趣的代码,下面代码打印出来是什么呢?

想知道打印出来的是什么,首先要知道printf函数返回什么。

printf函数返回的是打印在屏幕上的字符的个数。
上面的例子中,我们就第一个printf打印的是第二个printf的返回值,第二个printf打印的是第三个
printf的返回值。
第三个printf打印43,在屏幕上打印2个字符,再返回2。
第二个printf打印2,在屏幕上打印1个字符,再放回1。
第一个printf打印1。
所以屏幕上最终打印:4321。

二、函数的定义和声明

一般我们在使⽤函数的时候,直接将函数写出来就使用了。
比如:我们要写一个函数判断一年是否是闰年。

int is_leap_year(int y)
{
if ((y % 4 == 0 && y % 100 != 0) || (y % 400) == 0)
return 1;
else
return 0;
}

上面是函数的定义

这种场景下是函数的定义在函数调⽤之前,没啥问题。

但如果将函数的定义放到后面会出现警告:

这是因为C语言编译器对源代码进行编译的时候,从第一行往下扫描的,当遇到is_leap_year
函数调用的时候,并没有发现前面有is_leap_year的定义,就报出了上述的警告。
把怎么解决这个问题呢?就是函数调用之前先声明⼀下is_leap_year这个函数,声明函数只要交代清楚:函数名,函数的返回类型和函数的参数。
如:int is_leap_year(int y);这就是函数声明,函数声明中参数只保留类型,省略掉名字也是可以
的。代码变成这样就能正常编译了。

三、static和extern

static和 extern都是C语言中的关键字。
static是静态的的意思,可以用来:
• 修饰局部变量
• 修饰全局变量
• 修饰函数
extern是用来声明外部符号的。

讲解 static和 extern之前再讲一下:作用域和生命周期。
作用域(scope):限定这个名字的可用性的代码范围就是这个名字的作用域。

  1. 局部变量的作用域是变量所在的局部范围。
  2. 全局变量的作用域是整个工程(项目)。
    生命周期:指的是变量的创建(申请内存)到变量的销毁(收回内存)之间的一个时间段。
  3. 局部变量的生命周期是:进入作用域变量创建,生命周期开始,出作用域生命周期结束。
  4. 全局变量的生命周期是:整个程序的生命周期。

static修饰局部变量:

没有static时打印出来是这样的:

有了之后是这样的:

代码1:test函数中的局部变量i是每次进入test函数先创建变量(⽣命周期开始)并赋值为0,然后
++,再打印,出函数的时候变量生命周期将要结束(释放内存)。

代码2:中我们从输出结果来看,i的值有累加的效果,其实test函数中的i创建好后,出函数的时候是不会销毁的,重新进入函数也就不会重新创建变量,直接上次累积的数值继续计算。

结论:static修饰局部变量改变了变量的生命周期,生命周期改变的本质是改变了变量的存储类型,本来一个局部变量是存储在内存的栈区的,但是被 static 修饰后存储到了静态区。存储在静态区的变量和全局变量是⼀样的,生命周期就和程序的生命周期⼀样了,只有程序结束,变量才销毁,内存才回收。但是作用域不变的。

extern是用来声明外部符号的,如果一个全局的符号在A文件中定义的,在B文件中想使用,就可以使用extern进行声明,然后使用。

在add.c文件中定义了一个a,但它在test.c中不能使用,但如果用extern

就可以成功使用。

但如果我们用static修饰a就不可以使用。

一个全局变量被static修饰,使得这个全局变量只能在本源文件内使用,不能在其他源文件内使用。本质原因是全局变量默认是具有外部链接属性的,在外部的文件中想使用,只要适当的声明就可以使用;但是全局变量被 static 修饰之后,外部链接属性就变成了内部链接属性,只能在自己所在的源文件内部使用了,其他源文件,即使声明了,也是无法正常使用的。