摘要:本文对C语言中的函数进行全方面详细讲解,主要包括函数分类,函数参数,函数调用、函数声明和定义、函数递归、函数练习。全方面带你深度理解函数,耐心看完,相信看完这篇你会有不菲的收获~~
大家在学习C语言前都接触过数学中函数吧,数学里的函数描述的是一个过程,是一道重要考点,不要着急头疼 ~,二者没有太大联系,而在C语言中的函数被称为子程序,它用来实现某一特定的功能,比如增删查改。
注意:一个函数最好只能实现一个功能,如果一个函数能实现多个功能,这就是一个不好的现象。
官方名次解析:在计算机科学中,子程序(英语:Subroutine, procedure, function, routine, method,
subprogram, callable unit),是一个大型程序中的某部分代码, 由一个或多个语句块组
成。它负责完成某项特定任务,而且相较于其他代 码,具备相对的独立性。
一般会有输入参数并有返回值,提供对过程的封装和细节的隐藏。这些代码通常被集成为软件库。
库函数顾名思义是C语言库中的函数,是为了方便用户实现某些特定的功能而事先封装好的函数。
我们知道在我们学习C语言编程的时候,总是在一个代码编写完成之后迫不及待的想知道结果想把这个结果打印到我们的屏幕上看看这个时候我们会频繁的使用一个功能:
将信息按照一定的格式打印到屏幕上(printf)
2. 在编程的过程中我们会频繁的做一些字符串的拷贝工作(strcpy)
在编程是我们也计算,总是会计算n的k次方这样的运算(pow)
像上面我们描述的基础功能,它们不是业务性的代码。我们在开发的过程中每个程序员都可能用的到,
为了支持可移植性和提高程序的效率,所以C语言的基础库中提供了一系列类似的库函数,方便程序员进行软件开发。
首先要学会查询工具的使用:
MSDN(Microsoft Developer Network)
www.cplusplus.com
http://en.cppreference.com(英文版)
http://zh.cppreference.com(中文版)
以上是几个查询库函数的网站。
英文很重要。最起码得看懂文献
上图是一些库函数↑
简单的总结,C语言常用的库函数都有:
IO函数
字符串操作函数
字符操作函数
内存操作函数
时间/日期函数
数学函数
其他库函数
例如我上一篇博客中提到了time(时间戳函数)、rand(随机数生成函数)、srand(设置随机函数起点值函数)这些都是库函数,
(单击此处跳转→传送门)
注意:
但是库函数必须知道的一个秘密就是:使用库函数,必须包含 #include 对应的头文件。
自定义函数顾名思义就是自己定义的函数,这类函数发挥空间更大,库函数本质上也是被人事先定义好的函数。
如果库函数能干所有的事情,那还要程序员干什么?
所有更加重要的是自定义函数。
自定义函数和库函数一样,有函数名,返回值类型和函数参数。
但是不一样的是这些都是我们自己来设计。这给程序员一个很大的发挥空间。
先看一段代码
上图是一个简单的自定义函数实现,创建了一个自定义函数get_sum 功能是实现两数相加并把相加的值返回给主调函数,
其中函数名为 get_sum 返回类型为int 形参为 两个整形变量
注:这里只是一个自定义简单实现,具体函数调用实现和组成部分讲解在下面↓↓
真实传给函数的参数,叫实参。
实参可以是:常量、变量、表达式、函数等。
无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参
例如上图中 a, b变量为实参。
形式参数是指函数名后括号中的变量,因为形式参数只有在函数被调用的过程中才实例化(分配内存单元),所以叫形式参数。形式参数当函数调用完成之后就自动销毁了。因此形式参数只在函数中有效。
上图 x,y两个变量为形参
注意:形参实例化之后其实相当于实参的一份临时拷贝
函数调用实则是在栈区里开辟一段内存空间,这块内存空间被称作函数栈帧,这块知识属于汇编语言,这里简单提及下,主要为了说明不同函数用的是不同的函数栈帧属于不同段的内存空间,相对于独立。即不同栈帧里面的变量空间不会互相影响可以同名,这块知识主要为后面讲到递归适用。
函数调用操作: 函数名(调用需要传的参数)
注意:()里可以没有参数表示调用函数但不传实参,但调用时要加上() --为函数调用操作符>
函数的形参和实参分别占有不同内存块,对形参的修改不会影响实参。
下面看一道经典代码
swap函数功能设计是想让这个函数实现将a,b两变量中的数进行交换,经过调试发现 在函数里面 确实实现了两数交换,a,b中为2,4
而x,y中为4,2了,但是最后运行结果却是两数并没有交换。
这就证实了,形参实际是实参的一份临时拷贝,形参里面的数进行了相互交换,最后函数结束了形参所开辟的空间被释放了,而实参里的值并不受影响。
那这交换功能要如何用函数实现,这就要用到传址调用了↓
传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式。
这种传参方式可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操作函数外部的变量
我们可以看到,通过传址的方式实现了将a,b变量里两数的值进行交换。&a,&b为函数实参 为a和b内存空间的地址(即指针),将这两个地址传址调用函数形参 用整形指针变量接受这俩指针,最后通过解引用操作直接访问了a,b内存空间 ,将这俩个变量里的值进行互换。
告诉编译器有一个函数叫什么,参数是什么,返回类型是什么。
但是具体是不是存在,函数声明决定不了。 函数的声明一般出现在函数的使用之前。
要满足先声明后使用。 函数的声明一般要放在头文件中的。
声明外部函数需要关键字extern 一般可以省略,不清楚的可以看我之前一篇博客点击此处→传送门
主要形式为 extern 函数返回类型 函数名 (函数参数); 声明要加分号因为这是语句
函数的定义是指函数的具体实现,交待函数的功能实现
上图圈起来的为函数定义,由函数返回类型 函数名 (函数形参) {函数体} 组成
注意:在main函数上面定义的函数 表示同时声明且定义了,此时不同再声明就可以在其下面的函数中调用此函数,在main函数下方或其他源文件里定义的要事先在要调用此函数的函数上方声明该函数才能调用。 ()里的函数形参可以没有但括号不能省略,{}花括号里为函数体交代函数如何实现,可以为空 但{}不能省略,它们共同组成函数的整个定义 !!
函数和函数之间可以根据实际的需求进行组合的,也就是互相调用的。
嵌套调用即在一个函数里可以调用另一个函数,下面为嵌套调用代码示例↓
#include
void new_line()
{
printf("hehe\n");
}
void three_line()
{
int i = 0;
for(i=0; i<3; i++)
{
new_line();
}
}
int main()
{
three_line();
return 0;
}
注意:函数可以嵌套调用,但不可以嵌套定义!!
把一个函数的返回值作为另外一个函数的参数。下面是代码示例↓
#include
int main()
{
printf("%d", printf("%d", printf("%d", 43)));
//结果是啥?
//注:printf函数的返回值是打印在屏幕上字符的个数
return 0;
}
运行结果为4321,因为printf返回值为输出的字符个数,层层打印最后屏幕出现4321
程序调用自身的编程技巧称为递归( recursion)。 递归做为一种算法在程序设计语言中广泛应用。
一个过程或函数在其定义或说明中有直接或间接 调用自身的
一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解, 递归策略
只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。 递归的主要思考方式在于:把大事化小
存在限制条件,当满足这个限制条件的时候,递归便不再继续。
每次递归调用之后越来越接近这个限制条件
注意:如果没有满足限制条件,则会发生栈溢出问题!!
光说不练假把式,下面来做几道题巩固巩固~
下面为参考答案解析↓↓
此题思路是设一个num_jia1函数,实参为 num的地址 ,函数里形参用整形指针变量接受,通过解引用访问到 main里面的num变量使里面的数加1.
例如:
输入:1234,输出 1 2 3 4
下面的答案解析↓
做这道题如果不用递归思路而用循环思路需要更多的解题步骤,而递归只需要几步就解决了
可看到这串代码又有点蒙蔽, 接下来看图解↓
粗红线为 开始递归层层调用自身函数 ,细红线为最后返回得到的结果 ,每个函数里打印当前函数栈帧里的数除以10取余 得到其个位数,通过这种方法实现了打印一个整数的每位数
注意:递归是一种解题思想,不必强求一定得用递归解决,但需要了解递归。
恭喜你,看到这里,如果你把都理解了,你对函数的掌握已经到位了,但光有理论知识还不够,平时应该多加练习,做一些相关的题目,巩固知识,坚持学下去,你会看到不一样的自己,加油!!
最后,文章中有什么不对的地方还请指出,本人定当虚心接受,第一时间作出更正!!!
写文不易,给个一键三连,支持一下吧~~