C++语言基础——函数(详解)
目录
函数是什么
函数全名叫计算机函数,它可以帮助你完成一些特定的程序。你可以把它简单理解成一把手枪。
手枪的作用:完成几发子弹进行射击的功能
手枪的结构:有固定的几个部分构成
所以,手枪是具有一定格式,可以完成一些特定功能的一件工具,这就是函数
函数的定义
主函数举例
int main(){
cout << "Hello world!n";
return 0;
}
由上得,在创建函数时,必须编写其定义。所有函数定义包括以下组成部分:
- 名称:每个函数都必须有一个名称。通常,适用于变量名称的规则同样也适用于函数名称。
- 形参列表:调用函数的程序模块可以向其发送数据。形参列表是保存传递给函数的值的变量列表。如果没有值传递给函数,则其形参列表为空。
- 主体:函数的主体是处理函数正在执行的任务的一组语句。这些语句包含在一组大括号中。
- 返回类型:函数可以将值发送回调用它的程序模块。返回类型是要发送回的值的数据类型。
语法格式
返回值类型 函数名称 (参数列表)
{
函数体语句;
return 返回值;
}
空函数
如前所述,函数可以返回一个值。教程中介绍过的所有程序中的 main 函数都被声明为向操作系统返回一个 int 值。"return 0;" 语句使得当 main 函数完成执行时返回值 0
然而,并不是所有函数都一定要返回值。某些函数只需执行一个或多个语句,然后返回。在 C++中,这样的函数称为空函数,举个例子
void shuchu()
{
cout << "Hello world!n";
}
这个函数的名称为shuchu,它的命名方式和变量一样,函数应该按这种方式命名,即通过名称揭示其功能。因为该函数不需要接收任何信息以执行其任务,所以它的括号中没有形参列表
该函数的返回类型是 void。这意味着函数在完成执行后不返回值,并返回到调用该程序的部分。因为没有返回值,所以不需要 return 语句。当函数中的语句己经完成执行并且遇到结束函数的封闭大括号时,程序将自动返回
函数的调用
功能:使用已定义的函数
语法格式:
函数名称 (参数列表)
调用函数将导致函数的执行。函数 main 在程序启动时自动调用,但所有其他函数必须由函数调用语句执行。当一个函数被调用时,程序分支到该函数并执行其主体中的语句。
举例说明:计算x的n次方
#include<stdio.h>
#include<math.h>
double power(double x,int n)
{
int m;
m=pow(x,n);
return m;
}
int main()
{
double x,y;
int n;
scanf("%d%lf",&n,&x);
y=power(x,n);
printf("%.2f",y);
return 0;
}
运行结果:
在第3行,我们定义了一个名字叫power的函数,它的实际作用是求x的n次方,函数体内,使用了math库里的一个函数——pow函数,它的作用就是求x的n次方。在函数结束时,返回一个m的值
与所有 C++ 程序一样,该程序是从 main 函数开始执行的,其他函数只有在它们被调用时才执行。在上面程序中,函数 power 由 main 函数中的以下语句调用:
power(x,n);
递归
递归就是指一段程序直接或者间接调用自身的一种方法,通过这种形式执行需要进行一些固定步骤的许多操作,它可以把一个复杂并且庞大的问题简单化,通过专注于解决它分化出来的小问题从而解决大问题,从而大大减少我们的代码量,是提高我们编码效率的很好方法(递归就是容易理解,但是内存太高,大型考试建议不要用递归(除非不得不用,实在是做不出来))
递归的主要点
- 递归函数的参数和返回值
- 终止条件
- 递归的逻辑设计
举个例子(求阶乘):
int factorial(int n){
if(n==1)
return n;
else
return n*factorial(n-1);
}
int main()
{
int n;
cout<<"请输入整数:"<<endl;
scanf("%d",&n);
cout<<"整数:"<<n<<"的阶乘为:"<<factorial(n)<<endl;
cout<<"n"<<endl;
return 0;
}
这个看着不难,不就是个连乘吧?细想头大,就是让我用计算器去敲也要半天吧。
但有了递归,这个事情就好说了:
这个可以看成二个数相乘:100×(99×98×97×……×4×3×2×1)
括号内绿色的乘积看作一个数,简单了吧。
如果你还嫌烦,括号内的数也可以同样看成二个数相乘:
99×(98×97×……×4×3×2×1)
你没有没发现点什么?对的,我们就把一个相对复杂的式子看作一个整体,不去考虑它具体的值。
大家继续思考下,如果上面的括号里我还是嫌复杂呢?是不是还可以继续分成二个数相乘呢?答案是肯定可以的。
那分解到什么时候可以直接得到答案呢?
对的,如果分解成2×1,我们就可以轻松解决了。
那么3×(2×1)也就解决了。同理:
4×(3×2×1)也就解决了。同理呢?
大家应该能想到5×(4×3×2×1)也解决了,继续扩大就可以慢慢解决。
100×(99×98×97×……×4×3×2×1)
递归的结构
public void fun(参数) {
if (终止条件) {
return;
}
fun(参数);
(其他判断条件或语句);
}
在上边代码中,当第一次进入函数时,先判断是否符合终止条件,符合则直接结束函数,不符合入下一语句,调用自己重新进入下一层自身函数,(注意这是最外一层将不向下继续执行语句,外层卡在fun(参数处)),这个调用自己进入自身函数的操作过程即为“递”的过程。假设进入下一层后符合终止条件,返回结果,此时之前进入自身函数执行完成返回最外一层函数,最外一层函数递归调用处得到结果,(即内层函数执行完成得到结果返回值),这个过程即为“归”的过程。这时最外一层函数才能继续执行下一语句,直至函数运行完成。
条件
递归必须具备两个条件
一是有边界,即终止条件。
二是需要调用自己。
递归使用的场景
1.大问题可以拆分为多个子问题。
2.原问题和拆分后的子问题除了数据规模不同,解决思路完全相同。
3.存在递归终止条件。
递归在线性数据结构中使用不太明显,迭代基本可以很容易地解决问题。
递归在非线性结构中非常重要,比如二叉树,回溯,典型的树形问题-九宫格字母组合
使用函数时常见报错
1 重定义变量
Error:redefinition of 'a'
2 缺少分号
Error:expected ';' after expression
3 数组维数错误
Error:array type 'int [101]' is not assignable
4 关于 if 与 else
Error:expected expression
Warning: equality comparison result unused [-Wunused-comparison]
if 判断里不能有分号!
5 关于 if 与 else
这个是把等号写成了赋值号
Warning: using the result of an assignment as a condition without parentheses [-Wparentheses]
这个特别坑!!!
6 括号匹配错误
Error: expected ']'
Error: expected ']'
Error: extraneous closing brace ('}')
7 关于字符串的输入错误 (*)
Error: invalid operands to binary expression ('std::istream' (aka 'basic_istream<char>') and 'char *')
cin>>c+1;
~~~^ ~~~Warning: operator '>>' has lower precedence than '+'; '+' will be evaluated first [-Wshift-op-parentheses]
cin>>c+1;
~~~^~Note: candidate function template not viable: no known conversion from 'std::istream' (aka 'basic_istream<char>') to 'std::byte' for 1st argument
operator>> (byte __lhs, _Integer __shift) noexcept
^
8 写错函数 / 变量名
Error: use of undeclared identifier 'mam'; did you mean 'max'?
少年易老学难成,一寸光阴不可轻。未觉池塘春草梦,阶前梧叶已秋声