功能——将代码进行整体封装,方便后期调用的一种形式
故C语言是以函数的形式提供一个简洁的接口给用户的,用户只需要知道调用该接口即可实现某个功能即可。
格式:
修饰关键字(可以没有) 函数的返回值类型 函数名(函数的参数列表)
{
;//函数内容
return value;//函数的结束语句,其中value就是需要返回给调用者的返回值
}
例子:
int sum(int arg1, char arg2)
{
int sum = 0;
sum = arg1+arg2;
return sum;
}
解释:定义了一个函数,函数名字为sum,函数功能是实现两数的求和,函数返回两变量的和;
调用方法:x = sum(2,3); //即将2和3这两个实参传过去进行计算,函数返回两值之和给变量x;则x = 5;
修饰关键字
默认不加。常用的修饰关键字:static online ...
函数的返回值类型
用以表明该函数的调用会返回什么样的数据类型;
如果函数没有返回值用void去修饰;此时return这个语句也可以作为函数的出口,只是后面不需要写返回值;
定义函数之后,函数的调用的整条语句本身 就等价于“函数返回值类型”这样的数据。例如上边例子里的调用 sum(2,4) 这条语句本身就等价于一个整型int类型的数据,即 2+4 = 6;
若不写返回值类型,则系统默认为Int型;
函数返回值该使用什么类型可参考以下三种情况:
- 子函数运行几乎不可能出错、且我们不需要其返回值的的情况下,用void;
- 子函数我们明确的知道其可能运行成功,也可能运行出错,并且我们需要返回一个数值让我们知道它出错与否,这时用int;
- 我们需要函数返回一个具有实际意义的东西,我们需要用到的数据,这时会用到其他具体的数据类型。
函数名
该函数的名字,跟变量的命名规范是一样的;函数的命名要尽可能贴近函数所实现的功能
函数的参数列表
如果没有则写void,如果有则对应的声明各个参数的类型及名字
其本质就是在函数里面定义的变量,它的特别之处在于:它的初值由调用函数的人传给它,就是将实参的值传输给形参;即形参的值由实参来进行初始化,且该过程不可逆,即形参值不会逆过来传给实参。形参的改变也不会对实参造成任何影响。
函数里面的变量的特点:在函数被调用的时候生成,执行完函数之后会被销毁掉;
注意:
1、如果函数不需要传入参数,一般我们都会写上void,使得整个表达式更完整,利于代码阅读,当然不写语法也没错误,但不写的话这个函数头看起来就像函数调用的表达式,不利于程序的阅读
2、一般函数的参数个数不会超过6个;
3、传参和返回值时,只要类型匹配,什么东西(变量、语句等)都可以往里放;
4、同一次主函数执行里的不同次函数调用申请到的子变量所申请到的内存是不同的,因为系统的内存是动态变化的;
5、函数传参,从右到左执行
函数体
放你要执行的动作的代码。
Return
1、作为函数的结束标志,并且将后面的数据返回给调用该函数的人;
2、 return 后面是允许各种表达式及变量及常量的数据,只要类型与“函数的返回值类型”一致即可;
3、return后面只会返回一个值,并且只能放对应类型的数据;
4、 虽有返回值,但我们并不一定要去接收函数的返回值;
5、 不要去返回一个临时变量的地址,因为临时变量(函数里面定义的变量)在函数结束的时候就会被销毁,返回其地址肯定是不切实际的,但可以返回其值;
6、return通常作为函数出口;但就算不加return 只要函数执行完里边的所有代码也会结束。
函数的分类
主函数
main函数,一个程序有且只能有一个,程序运行由其开始由其结束。(但编译应该不是)
系统函数
由系统直接提供的函数,在man手册的第2本
库函数
这些函数已移植到系统中,在man手册第3本,例如printf 和scanf都是该库的;
自定义函数
平时自定义的函数,可实现过程复杂的功能
函数调用:
格式:函数名 (实参1、实参2....)
函数调用的整个表达式会返回一个函数内部的值;
函数调用时所传入的数据称之为实参;在函数的 格式声明 当中的参数列表里的参数称之为形参;
说明
1、 C语言中的函数和变量一样,在使用之前必须先声明之后才能使用。所谓声明,是指说明函数是什么类型的函数,一般库函数的声明都包含在相应的头文件里。
2、 嵌入式C中每个函数都是独立的代码块,函数代码归函数所有,出了函数调用外,其他任何函数中的任何语句都不能访问它。
3、 除非使用全局变量,否则一个函数内部定义的程序代码和数据不会与另一个函数内部的程序代码和数据相互影响。
函数诞生目的:
1,合理的将代码 集成 在一起,降低代码的重复率;
2,提高代码的 可读性;
3,提高代码的开发效率,实现一个接口实现一套动作,而不用去分心了解函数里面的内容
故函数的功能是要十分明确的,且函数不要面面俱到,只需确定实现有限少数功能即可,否则函数的封装也就失去了其意义。
注意
1,程序的入口永远都是main函数,并不会因为main所在的位置靠后而影响及执行过程
2,main函数里面尽量不要定义太多变量,因为变量会伴随整个程序运行终生,如此会有大量变量在整个运行周期里占用内存,造成内存的浪费
函数的声明的意义
函数的声明语句是为了:在函数调用时给 调用格式的使用 提供参照,让调用者知道要传输几个实参过去、要传的是什么类型的参数。
什么时候要加声明语句:
子函数放在主函数下边或在函数外部时一定要在主函数之前加上函数声明语句
声明类型:
内部声明 或 外部声明
内部声明语句,只需要将函数头放过来,用分号结尾即可
例如:void func(int);//其中,里面的形式参数列表中的变量名可以不写
外部声明语句,需要将函数头放过来,用分号结尾,并且以extern关键字开头即可
例如:extern void func(int);//声明这个函数不在当前.c文件中
函数的经典应用
回调函数——在本程序当中封装了函数的主体,然后给其他函数内部调用的 形式。
涉及到的概念:函数指针、函数名
应用场景:
在大型原码开发场合,统一工程文件的不同 .C文件里有不同的功能,但函数、全局变量等这些静态的东西是可互通共用的;全局变量是共用变量(内存),而回调函数就是实现共用功能;通过函数指针我们可以把某子函数的功能封装成一个接口,共给别的.C文件使用,所谓回调就是在我调用外部某一个.C的函数的时候,我们还把自己的子函数地址传给了对方,让对方在实现我们需要对方帮完成的动作的前提下,还能回过头来调用我方的子函数,实现我给对方的动作。
递归函数——函数自己调用自己
PS:除非特殊情况,不然少用递归
什么情况下慎用呢:
1,递归层数过多的情况最好不要用递归,无限申请内存,浪费内存
2,能用 for循环 去搞定的事情也不要用递归,因为递归需要消耗各种内存和指令
递归的调用窍门:
1,明确你的递归函数的功能是什么
2,在递归函数中只实现当前层数的功能,剩下的内容交给下一个调用自己的过程来做
3,递归函数一定要有出口 ,否则会无限循环!
4、C程序是线性的,递归也不例外。
5、注意!递归结束时,函数返回调用函数的地方,并且递归过去的变量值与当前层值无关,尽管后续程序的同一个变量进行了多次变化,已经面目全非,但一旦返回调用处,还是继续使用当前层被传过来的值!
变参函数:
格式:
返回值类型 函数名( const char *format, ...)
{
va_list start_arg; //诞生一个用来装参数位置的变量,规定一定是va_list这种类 型
va_start(start_arg, format); //获取接下来的参数位置,其中start_arg是以后的参数位置
会更新到这个变量中,format是第一个变参 函数的参数,起
到参考位置的作用
va_arg(start_arg, 什么类型分析); //获取变参函数format参数之后的参数,每获 取一次
,位置自动偏移到下一个,位置会更新 到start_arg这个
参数中
va_end(start_arg); //销毁参数的缓冲区
}
本质:传入参数个数可以变的函数
传参的参数中,第一个参数必须得是字符串,有两个作用:在变参函数中,作为参数的起始位置来去寻找之后的参数用的,指明后面的参数按照什么格式去分析他,有点像printf函数这类的用途,用变参函数需要导入头文件<stdarg.h>。
应用:
对于普通函数来说,我们需要指定固定的参数类型跟固定的返回值类型,普通函数在调用的过程当中我们需要经过压栈,内存申请,程序状态寄存器的 跳转;指令较少,且运算比较简单的场景下,用普通函数划不来
宏函数define——代码直接替换过来的函数
优点:
它是一种纯粹的替换,所以没有所谓的压栈,形参内存申请,程序跳转等步骤,效率极高
缺点:
1,程序无法保证形参的完整性
2,传入的参数无法保护
3,如果宏函数别人操作不当,容易发生致命bug,超级难解决bug
内联函数:
关键字:inline
替换的不是代码,而是指令,它就没有了程序跳转这一环节
对比宏函数的好处:
- 宏函数的3个缺点一个都没有
- 在普通的函数封装时,每次调用函数时,调用函数处都会进行保护现场再过去执行子函数,返回后还要进行回复现场,如此每次都会浪费一些运行时间,当调用过多时就会降低效率,而内联函数可以去掉这个时间,又可以封装函数;
- 在编译时,编译器会把内联函数的全部内容插入到调用处,即对于系统而言就相当于没封装一样,如此加快了代码运行速度,但对于我们而言确实已经封装了。
特点:
1,由它修饰的函数,会尝试变成一个内联函数(看你当前函数的内部效率, 由编译器自行决定)
2,需要在函数原型及声明语句当中添加inline关键字(声明语句是必须要加 的)
3,内联函数放到头文件里面,inline修饰内联函数的声明语句
使用步骤
- 将要进行转变的子函数放入一个与.c文件相同目录的自己创建的.h文件里,并在子函数头前加上关键字inline;
- 返回来在.c文件里添加该头文件,并进行该子函数的函数声明,但此处声明头前不用加关键字inline;
应用场景
主要用于大型开发场合的多函数封装场合,此时的内联函数会明显提高代码运行速度。
(以上所有均为个人学习所作笔记,若有疏漏、错误,欢迎指正,感谢您的阅读!)