0基础C语言保姆教程——第4节 函数

大家好~~~我是开心学编程,学到无极限的@jxwd😀

写在前面:

各位小伙伴还在为C语言的学习而苦恼嘛?

还在为没有知识体系而烦心嘛?

别急。因为~~~~

接下来的两个多月,我会持续推出C语言的有关知识内容。都是满满的干货,从零基础开始哦~,循序渐进😀,直至将C中知识基本全部学完🐂。关注我♥,订阅专栏 0基础C语言保姆教学,就可以持续读到我的文章啦😀🐕~~~~

本文为万字长文,满满干货。为防止找不到,可以收藏再看呦😀

本文为第4节——函数(文末附第一至第三节的文章链接)

在数学中,函数是我们常见的概念。你比如,y=f(x),这里的f 实际上就可以使认为函数的某种对应法则,使我们所给定的每一个x,都能得到我们想要的y。

而在C语言中,函数也是非常常见并且非常重要的。可以说,一个项目就是由若干个函数组成的。

如果一个程序员说他不会写函数,那真的很难想象他写的代码都是些什么东西。

那么既然函数那么重要,那我们就此开始来学习吧。

目录

C语言中函数可以分为两大类,分别是:库函数,自定义函数。

库函数

什么叫库函数

为什么会有库函数

 自定义函数

传值调用与传址调用

函数的嵌套调用和链式访问

函数的嵌套调用

函数的链式访问

函数的声明与定义

函数的声明

函数的定义

函数的递归

我们总结出递归的两个必要条件:


C语言中函数可以分为两大类,分别是:库函数自定义函数

库函数

什么叫库函数

库函数,其实也叫内置函数,就是其他人已经写好、为你封装好、你可以直接拿来用的函数。例如,我们之前的printf函数、scanf函数、strlen函数等等都是库函数(注意,这里特别强调,sizeof是关键字,不是函数!)

为什么会有库函数

原因很简单,对于一个项目在开发的过程中,那些常见的、经常被程序员拿来使用的函数,我们可以将其提供出来,让每一个程序员都能方便地用到,支持代码的可执行性,提高代码的执行效率。所以C语言提供了一系列库函数。

这样的话,比如当一个程序员在想使用printf的时候,就不需要频繁的对其定义,不需要每个人都哐哐写一堆让它怎么实现了。

那么,我们可以怎么学习库函数呢?

我们可以通过登录相关网站以及查阅相关文献来学习。

比如:我们可以点击下面的网站:

www.cplusplus.com

点开一看,咵~~~(图一)

(图一)

 好家伙,这么多。

都要记住么?

no。

我们可以简单的总结一下,常见的库函数有:

IO函数(输入输出函数)

字符串操作函数

内存操作函数

时间/日期函数

数学函数

其他库函数。

别急,这些函数,我们在后面的章节中都会介绍到。

其实,还有一些其它的网站,也是很好的学习资源。

我们在也这里给出来:

MSDN (Microsoft Developer Network)(这个大家可以上网搜,下载安装包)

http://en.cppreference.com(英文版)

http://zh.cppreference.com(中文版)

我们以MSDN为例,来具体的操作一下,看看怎么借助这些资源、网站来学习这样的库函数。(其他的网站也是大同小异)(如图二)

(图二) 

我们在查找栏里面随便输入一个我们要找的库函数,我们这里就以strlen为例。

(图三)

如上图(图三)所示:

那么,我们具体来分析一下,如下图 (图四):

  (图四)

下面的实际上正是函数的具体的调用方法(图五):

 (图五)

自定义函数

如果库函数能干所有的事情,还要自定义函数干嘛?

所以,库函数显然是不能把所有的事情都包圆的。我怎么知道你要实现的具体功能到底是什么,并且不同的人的需求区别也都大了去了。所以这个时候,自定义函数登场了——它相较于库函数而言更加灵活,可以实现自己想要实现的功能。与此同时,这也给了程序员很大的发挥空间。

自定义函数和库函数一样,有函数名,函数返回类型以及参数的类型。

关于自定义函数,我们在第二节实际已经提到过了。我们在这里再来说一遍:

具体的格式是这样的(如图六) :

(图六) 

在花括号里面便可以实现自己想要实现的代码内容。

还是那个Add函数的例子(如图七):

(图七)  

(注:这个例子十分典型,我当时一次性把最终版做出来了,我比较懒,

直接弄过来了。不想再重画一遍这个例子了,还请见谅哈哈。当然我们下面会有新的例子~~~~)

ok。我们再来说说函数的调用的方法:

传值调用与传址调用

掌握了函数的基本使用方法后(也就是说你可以咋你的电脑上正确地敲出add函数来的时候),我们需要对于函数的调用作更深层次地讨论:

我们先来看这么一个例子:

我们的目标是要实现一个函数来交换两个变量的值:(即交换变量a,b的值)(如图八)

(图八)  

我们先来分析一下这个swap函数(如图九)

(图九) 

那结果是不是我们想要的那样,把值给交换过来了呢?

我们运行一下试试看:(如图十)

(图十) 

 我们神奇地发现,交换前后a,b的值并未发生任何改变。这是怎么回事?是我们交换变量的值的时候逻辑错了么?显然不是。这就和函数传参的特点有关系了。

实际上,函数在传参时,我们会为形参重新开辟一份新的内存空间。(别告诉我形参不知道是啥~~哈哈,如果不知道去看一下第二节,找到函数的部分)那么,函数中实参的值会赋给形参。​​​​​​0基础C保姆自学 第二节——初步认识C语言的全部知识框架_jxwd的博客-CSDN博客

 具体来说,可以是这样的:

在创建函数的形参和实参时,我们可以理解成  为函数的参数  在栈区上开辟了空间。(如图十一)

(图十一) 

 所以,我们有着这样一句标准的说法:

函数的形参是实参的临时拷贝,改变函数的形参不会对实参产生影响。

函数的形参是实参的临时拷贝,改变函数的形参不会对实参产生影响。

也就是说,我们刚刚的交换操作只是对pa,pb进行的,完全不干a,b的事情。

不信?我们可以通过调试来看一下具体的情况:

按F11(或者F10,但F10在调试的过程中不会进入你自己写的函数)打开调试->窗口->监视->监视1(2,3,4也行,随你便)我们在监视框内输入:a,b,pa,pb,不断按f11,让代码一步一步走,来观察这四个值的变化:(如下图(仅展示了关键的步骤))

(图十二)  

我们可以清晰地看到(图十二),当函数执行完毕后,pa,pb的值发生了交换,单数a,b的值没有发生任何改变。

这个实际上,就是函数的传值调用

那么我们就是想要通过函数来交换怎么办呢?

我们可以采用传址调用的方法:

请看:(图十三)  

(图十三)  

这个时候,我们可以选择用指针来去实现(有关指针的初步介绍已经在第二节探讨过)

这样写具体的意思是什么呢?

我们可以这样来理解:(图十四)

(图十四) 

首先, 我们分别输入&a和&b,获得到a和b的地址。在本次编译中,我们获得到

a的地址为0x00F3FDEC

b的地址为0x00F3FDE0

ok,我们用刚刚拿到的地址来接着分析(图十五):

(图十五) 

 这样,我们的pa,pb就拿到的是a,b的地址。

我们后面的*pa是通过拿着a的地址,找到a中存储的变量的值。(*pb同理)(指针相关的知识在这里不再赘述,详见第二节,当时已经做好了铺垫,在这里主要介绍传址调用)

这样,我们接下来进行的交换变量的值所做的操作,正是对a,b本身进行操作的。

因此,当我们再次打印a,b变量的值的时候,得到的便是交换后的值。

总结一下:

还是那句话函数的形参是实参的临时拷贝,改变函数的形参不会对实参产生影响。

当我想要对实参进行操作的时候,我必须要把其地址传过去,这样在函数中,便可以通过指针解引用来找到我想要操作的变量。

函数的嵌套调用和链式访问

函数的嵌套调用

其实就是字面意思:在函数中调用函数。

举一个非常简单的例子:(图十六)

(图十六) 

 请问会打印什么?

答:三行hehe。原理很简单,和我们之前add函数的调用的思路是一样的。

(图十七) 

 这样是完成了一次hehe的打印(图十七)。然后继续进入循环,总共完成3次hehe的打印。

再来看链式访问:

函数的链式访问

什么叫链式访问?我们可以通过下面一个例子来说明:

(图十八)  

像这样(图十八),把一个函数的返回值作为另外一个函数的参数的形式,叫做函数的链式访问。

那么请问,上面(图十八)的结果是什么?

我们可以按住ctrl(+fn)+f5让代码运行(图十九)。

(图十九)  

为什么?其实只要弄明白一点就很简单了:就是printf的返回值是什么?

不会有人会问:难道printf还有返回值?

问的好!我们就来看看printf到底有没有返回值。

我们选中printf,右击鼠标,点击转到定义,(图二十)

(图二十) 

就会看到这么一坨东西:(图二十一)

 (图二十一) 

 其它什么都不认识,就认识printf和int 就够了。看到没?返回类型为int(因为其它的又是一个又一个的定义)

还不服?我们打开msdn,输入printf(图二十二) :

(图二十二)  

看,上面清清楚楚地写着返回值为int

那好,既然是int ,那应该返回多少呢?

在msdn刚刚的界面里往下拉,看到这一行(图二十三):

(图二十三)  

简单翻译一下,就是说它的返回值是“the number of characters printed",即打印的字符个数。(如果发生错误则返回一个负数)

那么我们再看刚刚的那个例子,应该就很明确了(图二十四) :

(图二十四)  

所以,在屏幕上打印的应该为4321。

函数的声明与定义

函数的声明

函数声明的作用是什么?

就是告诉编译器,我有这么一个函数。函数的名字叫什么,参数的类型是什么,返回的类型是什么……

但是,是否真的存在这么个函数,函数的声明决定不了。那是由什么决定?由马上我们所说的函数的定义决定。

函数的声明要放在函数使用之前。

函数的声明一般放在头文件中。满足先声明,后使用。

函数的定义

函数的功能的具体实现,就是函数的定义的过程。

我们把函数的声明和定义结合起来,举一个例子(图二十五):

(图二十五) 

至于分文件操作,我们等到写扫雷的时候再来具体实现。 

函数的递归

我们再来说最后一个——函数的递归

函数的递归是函数的嵌套调用的一种特殊形式:自己调用自己。俗称套娃。

来看一个例子:(图二十六)

(图二十六) 

 来思考一下,若我输入123,会输出什么呢?(图二十七)  

(图二十七)  

这就是函数的递归调用所实现的。

我们来具体的分析一下:(我们把print函数复制了两份来演示,便于读者理解)

(如下图) (图二十八)

(图二十八) 

好。那么递归能不能这样呢?(图二十九) 

(图二十九) 

 不断地调用main函数自己本身?

理论上这是可以的。但是这个程序永远都停不下来。因为其将会无限次地递归下去。

所以,为了避免这样死循环的局面出现,

我们总结出递归的两个必要条件:

1、存在限制条件,当满足这个限制条件的时候,递归便不再继续。

2、每次递归调用之后越来越接近这个限制条件。

上面的例子中,就是因为调用main函数没有条件限制,所以会导致死循环。

另外,我们还需要知道递归的价值:函数递归会使用大量的空间,因为它会不停地调用某一个函数,但是在递归的时候,内存中为上一级函数开辟的空间并不会被销毁,因为该函数还没有结束。所以,在递归下一级函数的时候,内存就必须要为其在新的地方重新开辟一块空间来。

但是,递归有利于将复杂的问题简单化。采用大事化小的思考方式,大大便捷了思考的过程。

所以,在日后的代码实现中,是否使用递归,还需要根据具体的情况来定。

好啦,本章的内容暂且就到这里啦,关注我@jxwd,我们下节再见😀~~~

如果觉着好,就赏一个赞吧~~~~    各位的肯定就是作者创作最大的动力😊

(文末附第一至第三节的文章)

C语言自学保姆教程——第一节--编译准备与第一个C程序_xdnxl的博客-CSDN博客

0基础C保姆自学 第二节——初步认识C语言的全部知识框架_xdnxl的博客-CSDN博客

0基础C语言自学教程——第三节 分支与循环_xdnxl的博客-CSDN博客

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jxwd

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值