本笔记将深入探讨一下函数的相关概念以及例题,有助于你理解函数的基本结构以及它是如何进行实现的,进而实现自己编写函数。本章主要要掌握函数的基本使用和递归。
1.函数是什么
2.库函数
3.自定义函数
4.函数参数
5.函数的调用
6.函数的嵌套调用和链式访问
7.函数的声明和定义
8.函数递归
首先什么是函数?
数学中我们常见到的函数的概念。但是你了解C语言中的函数吗?
维基百科对函数的定义为:子程序
在计算机科学中,子程序某项是一个大型程序中的某部分代码,由一个或多个语句块组成。它负责完成某项特定任务,而且相较于其他代码,具有独立性。
一般会有输入参数并有返回值,提供对过程的封装和细节的隐藏。这些代码通常被集成为软件库。
C语言中函数的分类:
1.库函数
2.自定义函数
库函数:
为什么会有库函数?
1.我们知道在我们学习C语言编程的时候,总是在一个代码编写完成之后迫不及待的想知道结果,想把这个结果打印到我们的屏幕上看看。这个时候我们会频繁的使用一个功能:将信息按照一定的格式打印到屏幕上。
2.在编程的过程中我们会频繁的做一些字符串的拷贝工作。
3.在编程时我们也计算,总是计算n的k次方这样的运算。
像上面我们描述的基础功能,它们不是业务性的代码。我们开发的过程中每个程序员都可能用的到,为了支持可移植性和提高程序的效率,所以c语言的基础库中提供了一系列类似的库函数,方便程序员进行软件开发。
这里推荐一个学习库函数的网站:www.cplusplus.com
strcpy 复制字符串
memset 内存设置
自定义函数:
自定义函数和库函数一样,有函数名,返回类型和函数参数。但是不一样的是这些都是要我们自己来设计。
例题:
1.写一个函数可以找出两个函数中的较大值
2.写一个函数可以交换两个整型变量的内容
为什么上述的代码输出的结果错误?
看上图,可知,x,y 和 a,b的地址不同,可见它们属于两个不同的空间,所以对于函数内x,y的替换并不影响a,b的值,所以我们应该对于a,b的地址进行修改,就比如
输出的结果为20
对之前的代码进行修改,如下图
而如果需要把函数处理结果的两个数据返回给主调函数,则有以下三种方法:
1.可在形参中用数组
2.可在形参中用两个指针变量
3.用两个全局变量
函数的参数:
什么是实际参数(实参)?
真实传给函数的参数,叫实参。实参可以是:常量、变量、表达式、函数等。无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传给形参。
什么是形式参数(形参)?
形式参数是指函数名后括号中的变量,因为形式参数只有在函数被调用的过程中才实例化(分配内存单位),所以叫形式参数。形式参数当函数被调用完成之后就自动销毁了。因此形式参数只在函数中有效。
例如:
&a,&b为实参
int* pa ,int* pb为形参
函数的调用:
具有传值调用和传址调用两种方式
传值调用:
函数的形参和实参分别占有不同内存块,对形参的修改不会形象实参。
传址调用:
传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式。
这种传参方式可以让函数和函数外部的变量建立起真正的联系,也就是函数内部可以直接操作函数外部的变量。
写一个函数,实现一个整型有序数组的二分查找:
函数的嵌套调用和链式访问
嵌套调用:
链式访问:
例:
为什么输出的结果为 4321 ?
这就要了解一下 printf 的返回类型
返回类型为 int ,函数返回的值是 打印在屏幕上的字符的个数
printf (" %d ",printf ("%d", printf ("%d",43)));
现在先看最内层的 printf 函数,它的打印值为 43 ,共两个字符,所以返回的为 2,而 2 就成为下一个函数的打印值,第二的也就是绿色的 printf 返回的值就为 1,作为第一个函数的打印值,最终的结果为4321
函数的声明和定义
函数声明:
1.告诉编译器有一个函数叫什么,参数是什么,返回类型是什么,但是具体是不是存在,无关紧要。
2.如果函数放在结尾,则在函数使用之前需要放上函数的声明,满足先声明后使用。
3.函数的声明一般要放在头文件中的。
函数的定义:
函数的定义是指函数的具体实现,交代函数的功能实现。
如果函数的定义放在开头的话,就不需要函数的声明了
函数的声明就相当于 告知你我有这个函数 ,因为代码的运行是从上往下的,如果函数的定义在开头,程序就会直接识别这个函数,不需要声明。而如果在后面的话,当程序运行到指定函数时,就无处识别,但加上声明,就相当于 告诉它我有这个函数它在最后面。
Sub.h 进行声明 Sub.c 进行实现
在头文件中对函数进行声明,然后在源文件中对代码进行完善,进行函数的实现。
函数递归
什么是递归?
程序调用自身的编程技巧成为递归。递归作为一种算法在程序设计语言上广泛应用。一个过程或函数在其定义或说明中有直接或间接的调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可以描述出解题过程所需要的多次重复计算,大大的减少了程序的代码量。递归的主要思考方式在于:把大事化小
递归的两个必要条件 (当工作量太大,有可能会栈溢出)
1.存在限制条件,当满足这个限制条件的时候,递归便不在继续。
2.每次递归调用之后越来越接近这个限制条件。
练习一:(画图讲解)
接受一个整型值(无符号),按照顺序打印它的每一位。例如:输入:1234,输出:1 2 3 4
递归 - 函数自己调用自己
期间最为重要的为以下的两个条件
if 语句约束 print 函数,避免它陷入死循环
print 函数则不断地逼近跳出条件
但不是说这两个条件存在就百分百正确,就比如下面这个例子
这儿存在一个问题 - 栈溢出
那么什么是栈溢出呢?
栈溢出指程序在执行过程中,由于递归调用过深或者局部变量过多等原因,导致程序的调用栈)超出了其分配的内存空间。在计算机程序中,栈是一种后进先出(LIFO)的数据结构,用于存储程序执行期间的临时变量和函数调用信息。
内存分为栈区 、堆区、静态区
在内存的栈区由于递归调用过深或者局部变量过多超出了内存的空间,导致内存的空间不断的缩小直到栈溢出,影响输出的结果。
所以写递归代码的时候:
1.不能死递归,都有跳出条件,每次递归逼近跳出条件
2.递归层次不能太深
这里推荐一个程序员的知乎网站:https://stackoverflow.com/ 在上面可以询问编程大佬一些你的疑惑点,促进你对知识的掌握。
练习二;(画图讲解)
条件: 编写函数不允许创建临时变量
问题; 求字符串的长度
比如说,求" bit "的字符串长度
我们可以先思考一下,应该怎样去编写
my_strlen ( bit )
1 + my_strlen ( it )
1 + 1 + my_strlen ( t )
1 + 1 + 1 + my_strlen ( \0 )
然后通过代码去实现
练习三 :求n的阶乘(不考虑溢出)
a.利用 for 循环实现
b.利用递归的方法
比如说: 求5的阶乘
5 * Fac (4)
5 * 4 * Fac (3)
5 * 4 * 3 *Fac (2)
5 * 4 * 3 * 2 Fac (1)
练习四:求第n个斐波那契数。(不考虑溢出)
首先什么是斐波那契数?
1 1 2 3 5 8 13 21 24 55 ......
像这样,后一个数字是前两个数字的和,就被称为斐波那契数。
比如说: 求第五位的斐波那契数
5
4 + 3
3 + 2 + 2 + 1
2 + 1 + 1 + 1 + 1
1 + 1 + 1 + 1 + 1
最终的输出结果为 5
但这种方法效率太低
比如说,我们计算第 10 个斐波那契数,看一看第 3 个斐波那契数需要计算的次数
这就说前面一直在重复的计算,导致效率太低
省略掉前面重复的运算,只保留结果(迭代方法),这样就能提高效率了
1 1 2 3 5 8 13 21 24 55 ......
经典例题:1.汉诺塔
2.青蛙跳台阶:n个台阶共有多少种跳法
练习五:
计算1/1-1/2+1/3-1/4+1/5......+1/99-1/100的值,打印出结果
方法一:
方法二:
练习六:
求10个整数中的最大值
练习七:
在屏幕上输出9*9乘法口诀表
%2d 这是一个格式说明符,用于 printf 或 函数中,表示一个整数。%2d 中的 2 表示最小字段宽度为2,如果整数的位数少于2,它将用空格填充至2位宽。如果整数的位数超过2,它将正常显示所有位数
%-2d 这里的 - 符号表示左对齐输出
练习八
函数调用exec((v1,v2),(v3,v4), v5 , v6 ); 中实参的个数为4个
像(v1,v2)这样的式子叫做逗号表达式,这是C语言中的一种表达式,它允许你将多个表达式用逗号分隔开来,并且会按照从左到右的顺序依次计算每个表达式。逗号表达式的值是最后一个表达式的值。
练习九
编写一个函数 reverse_string (char* string ) (递归实现)
实现:将参数字符串中的字符反向排列,不是逆序打印
要求:不能使用C函数库中的字符串操作函数
比如:
char arr [ ] = " abcdef "
方法一:
其中,还有一种地址的写法,如下
方法二:(运用递归)
练习十:
计算一个数的每位之和(递归实现)
写一个递归函数DigitSum(n),输出一个非负整数,返回它的数字之和
例如:调用DigitSum(1729),则应该返回1+7+2+9,它的和是19
思考过程:
DigitSum(1729)
DigitSum(172) + 9
DigitSum(17) + 2 + 9
DigitSum(1) + 7 + 2 + 9
返回 1 + 7 + 2 + 9
要点:要用递归时,先写出思考过程然后根据思考过程用代码实现(详细过程可看上述的例题)
例题十一:
编写一个函数实现n的k次方,使用递归实现
如果是2 的-3次方该怎样实现:
我们可以把-3 次方变为3次方,然后继续在k>0中递归
随着这篇文章的结束,我们对函数的探索也暂时告一段落。希望我的分享能够为你带来启发,让你对函数有了更深的理解和认识。知识是无价的,它不仅能够丰富我们的内心世界,还能指引我们前行的方向。我坚信,通过不断的学习和分享,我们能够共同成长,一起进步。
如果你对今天的内容有任何疑问,或者想要进一步探讨相关话题,请随时在评论区留下你宝贵的意见和问题。我期待与你的每一次交流,因为每一次对话都是知识的火花,都有可能点燃新的思想。
感谢你抽出宝贵的时间阅读这篇文章。如果你觉得这篇文章对你有所帮助,不妨分享给你的朋友和同事,让知识的力量传播得更远。同时,也欢迎你关注我的其他文章,让我们在知识的海洋中继续航行。
最后,愿你的每一天都充满学习的乐趣和成长的喜悦。我们下次见!