转载知呼大佬06 - C++函数 - 知乎 (zhihu.com)
06 - C++函数
本期我们讨论的是 C++ 中的函数。
函数到底是什么呢,函数就是我们写的代码块,被设计用来执行特定的任务,以后我们学习 class 类的时候,这些块会被称为方法,但是当我说到函数时,我们讲的可不是 C++ 类里面的 方法。
01 函数能干什么
对我们来说,使用函数是很常见的,避免代码重复。我们不想重复写相同的代码,当然,如果我们也可以复制和粘贴很多代码,但会导致巨大的混乱,这也意味着如果我们决定改变一些代码,我们必须在所有这些地方改变它,我们粘贴的地方,那维护这些代码简直就是灾难,所以我们把我们要做的事情写成一个函数,然后可以多次调用它。
如果需要的话,你可以认为函数有一个输入,一个输出,尽管他们可能并不需要这么做。我们可以为函数提供一定的参数,函数可以为我们返回值。
02 定义函数
假设我们想把两个数相乘,我们想写出这样的一个函数。
我们对上面的代码做一些详细的解释。
首先写的是所谓的返回值 int
,也就是说,这就是这个函数的返回类型,因为是两个整数相乘,这当然会得到一个整数 ;然后给出函数名 multiply
,它有两个参数 a
和 b
,这些就是我们要乘的数,它们的类型都是 int
。然后给出函数体,它会返回 a*b 。
这样你就得到一个函数,它有两个都是整数的参数,该函数的功能是返回两者的乘积。
我们不一定非要提供参数,例如,我可以不提供任何参数,直接返回 5*8 ,这仍然是一个返回整数的函数,但它没任何参数。
我们也可以让函数不返回任何东西,我们通过写 void 作为返回类型来实现,void 意思是啥也没有,所以我们可以这样做。
03 调用函数
如何调用这个函数呢?调用一个函数非常简单,我们直接看下面的例子。
对上面的代码我们做一些解释。
首先我们定义了一个变量 result
,来装这个结果,然后直接写函数的名字加括号就可以完成调用函数,括号里面的数字就是函数需要的两个参数,返回值 result 保存 a*b 的结果,最后我们将结果输出到控制台。按 F5
来运行程序,你可以看到我们得到显示是6,也就是2乘以3的结果。
04 为什么要用函数
所以,为什么要用函数呢?看起来代码还更多了一些。
让我们把情况说的更详细些,假设我要做一堆的乘法,我想把它们都记录到控制台,在没有函数的情况下,下面的情况这样看起来会很乱。
如果你运行了这些代码,你会发现我犯了一些错误,我想说的是,其实这种情况经常发生。人们复制和粘贴代码块, 然后忘记改变一个小细节,在某些情况下程序如果正常运行的话,你就有可能注意不到,直到它在某个地方出错了,你才会知道。
(你不会还没发现错在哪里吧?)
让我们修正这些代码。
这样我们会得到正确的结果。
上面的多段代码大部分内容基本差不多,基本的区别就是参与乘法的数字不同。这就很难受了。
让我们为这个重复的过程创建一个函数。
先分析一下具体的需求。这个函数应该不需要返回值,它只需要输出一些信息;这个函数需要两个参数,就可以完成每段代码中不同的部分;为了直观的联想到函数的功能,函数的名字就叫做 MultiplyAndLog
吧,然后我们就有了下面这个函数。
函数定义好后,我们要做的就是调用这个函数。
如果启动这个程序, 你可以看到,在这里可以获得正确的值,这是一个很简单的例子,我们获得了一个非常干净、易于阅读的程序。
通过以上给分析过程,我们有效地证明了函数是真的非常重要的。你的目标应该是将你的代码分开成很多函数。
然而, 有一件事我想强调,也不要把你的代码每一行都拆成函数,对任何人都没有好处的代码,这很难维护,您的代码将看起来凌乱不堪,它会让你的程序变慢。
原因是每次我们调用函数时,编译器生成一个call 指令,这基本上意味着,在一个运行的程序中,我们需要创建一个堆栈结构,这意味着我们必须把像参数这样的东西推进堆栈,我们还需要将一个叫做返回地址的东西,然后我们要做的是跳到二进制执行文件的不同部分,以便开始执行我们的函数指令。为了将 push 进去的结果返回,我们得回去到最初调用函数之前。就像在内存中跳跃来执行函数,跳跃和执行这些都需要时间,这些会减慢我们的程序。当然前提是保持我们当前的函数是作为一个实际的函数,而不是做内联inline
的工作(我们会在未来深入讨论内联)。
我说这些都是因为不想让你们对于每一行代码都去创建函数,不要做那么搞笑的事情,编码多了你才能明白哪里你会需要一个函数,但基本上如果你看到自己在多次做一项共同的任务,那么基本可以为此创建一个函数,函数的主要目的是防止代码重复,我们不希望复制粘贴代码。
05 其他的
现在我们回到代码中,有些小伙伴会发现一个细节,main 函数需要返回 int 类型的值,可是在 main 函数体中却找不到 return 关键字。这样操作是合理的吗?
然我们做一个修改,验证一些东西。
Ctrl+F7 编译,我们得到了一个错误。
看起来有返回类型的函数的确必须有返回值。
原因是主函数实际上是一个特殊函数,只有主函数被豁免可以不返回值,它会自动假设你返回0。其他的函数必须要返回值。 main函数不写返回值的操作只是现代 C 和C++ 的一个特性,可以让你代码保持的干净一点。
好了,我们知道这必须返回一个值,刚才讲的东西实际上 只适用于调试模式,如果我们在release 模式下编译,这里你会看到我们没有得到错误,这并不是说我们现在做的是正确的,因为如果我们真的要拿返回值值做某些事情的话,我们会得到 “未定义的行为” 的报错信息,只是编译器不会报错,但是在调试模式下,编译器就会显示出错。
后话
好了,这是一个基本的函数介绍,函数真的很有用。未来我还会写很多函数,每个程序都是由一系列函数组成的,所以这是非常重要的,如果你不认为你完全理解函数是如何工作的,不用担心, 因为在这个系列中我们会写超级多的函数,你会掌握它的,最好的学习方法当然是多练习是吧。我们通常还将函数分解为声明和定义,声明通常存储在头文件中,我们在转换单元或 CPP 文件中编写定义,我将专门制作关于头文件的一期,在那里会讲到在头文件中函数如何声明。