函数让代码变得更加简洁。
一、函数 一个典型的函数定义包括以下部分:返回类型、函数名字、由0个或多个形参组成的列表以及函数体。
(1)编写函数
编写一个求阶乘的程序为例:
函数名字:fact,
形式参数:1个整型参数val,
返回值:返回1个整型值。
int fact(int val){//返回类型int,函数名fact,形参int val
int ret = 1;
while(val>1) ret*=val-- ;
return ret; //返回阶乘结果,结束fact函数
}
(2)调用函数 函数的调用完成两项工作:
❶、用实参初始化函数对应的形参,
❷、将控制权转移给被调用函数。此时,主调函数的执行被暂时中断,被调函数开始执行。
int main(){
int j = fact(5);//调用上面定义的fact函数,传入实参5
cout << "5! is " << j << endl;
return 0;
}
(3)形参和实参
◆ 实参是形参的初始值。第一个实参初始化第一个形参,第二个实参初始化第二个形参,依次类推。
◆ 形参和实参的类型和个数必须匹配。
fact(“hello”); // 错误:实参类型不正确
fact(); // 错误:实参数量不足
fact(42, 10, 0); // 错误:实参数量过多
fact(3.14); // 正确:该实参能转换成int类型,等价于fact(3);
◆ 形参也可以设置默认值,但所有默认值必须是最后几个。
◆ 当传入的实参个数少于形参个数时,最后没有被传入值的形参会使用默认值。
(4)函数的形参列表 函数的形参列表可以为空,但是不能省略。
void f1() {/* …. */} // 隐式地定义空形参列表
void f2(void) {/* … */} // 显式地定义空形参列表
形参列表中的形参通常用逗号隔开,其中每个形参都是含有一个声明符的声明。即使两个形参的类型一样,也必须把两个类型都写出来:
int f3(int v1, v2) {/* … */} // 错误
int f4(int v1, int v2) {/* … */} // 正确
(5)函数返回类型
大多数类型都能用作函数的返回类型。一种特殊的返回类型是void,它表示函数不返回任何值。
函数的返回类型不能是数组类型或函数类型,但可以是指向数组或者函数的指针。
没有返回值的return语句只能用在返回类型是void的函数中。返回void的函数不要求非得有return语句,因为在这类函数的最后一句后面会隐式地执行return。通常情况下,void函数如果想在它的中间位置提前退出,可以使用return语句。return的这种用法有点类似于我们用break语句退出循环。
只要函数的返回类型不是void,则该函数内的每条return语句必须返回一个值。return语句返回值的类型必须与函数的返回类型相同,或者能隐式地转换函数的返回类型。
(6)变量的作用域 作用域就是变量有效作用的范围,作用域限定了变量的使用范围。根据作用域的不同,分为局部变量和全局变量。
全局变量是指定义在函数之外的变量,不被任何函数体{}所包含。全局变量在全局有效,被所有函数所共用。
局部变量是在一个函数内部定义的变量。局部变量只在当前所在花括号{}内有效,{}外无法访问该变量。作用域结束,局部变量即被销毁,内存空间就被释放。
局部优先原则:定义在函数内部的变量名称,会自动屏蔽定义在外部作用域下的相同变量名,优先访问到局部变量!
初始化:局部变量定义后,初始值不会自动初始化,必须人工初始化,全局变量系统会自动赋初值。
int:0、double:0、char:’\0’(空字符,对应的 ascii 是 0)、bool: false
int 数组:0、double 数组:0、char 数组:’ \0’、bool 数组:false
全局变量的变量名不能和系统函数同名,比如:max、min…
全局变量的值所有函数共享,局部变量的值只能在该函数中使用。
全局变量中,可以定义更大的数组。
在函数内部定义数组,最大可以开1Mb~2Mb,大约是500000个整数的大小。
在函数外部定义数组,注意解题一般给定的内存是256Mb (约6*107个整数)。
建议在做题时,将变量定义为全局变量,以使得变量默认自动初始化,可以开更大的数组空间。
二、函数参数传递的三种方法:传值、传引用、传指针
(1)传值参数 当初始化一个非引用类型的变量时,初始值被拷贝给变量。此时,对变量的改动不会影响初始值。
(2)传引用参数 当函数的形参为引用类型时,对形参的修改会影响实参的值。使用引用的作用:避免拷贝、让函数返回额外信息。传参的过程给实参起别名,形参作为实参的别名,相当于传递变量本身,形参和实参是同一个变量!这种传递参数的的方式称为引用传递。
(3)传指针参数 定义函数时指针作为形参,在函数调用时传递变量的地址。
另外需要注意的是,数组的形参在函数中对数组中的值的修改,会影响函数外面的数组。一维数组形参的写法:
// 尽管形式不同,但这三个print函数是等价的
void print(int *a) {/* … */}
void print(int a[]) {/* … */}
void print(int a[10]) {/* … */}
多维数组形参的写法:
//多维数组中,除了第一维之外,其余维度的大小必须指定
void print(int (*a)[10]) {/* … */}
void print(int a[][10]) {/* … */}
int print(int a[]){ //形参a[]
for(int i=0;i<=10;i++) cout<<a[i]<<endl;
}
int print(int a[][10]){ //形参a[][10]
for(int i=0;i<=10;i++)
for(int j=0;j<=10;j++)
cout<<a[i][j]<<endl;
}
三、函数重载 是定义多个具有相同名字的函数,但是形参个数或者类型不能相同。
四、函数递归 在一个函数的内部,函数也可以调用函数自己。
#include <iostream>
using namespace std;
int fact(int n){ //求n的阶乘
if(n==1 || n==0 ) return 1; //边界:n为1或0时,返回1
return n*fact(n-1); //返回n * (n-1的阶乘)
}
int main(){
int a;
cin>>a; //输入正整数n
cout<<fact(a)<<endl;
return 0;
}
五、常用的数学函数
头文件 #include<cmath> 或 #include<math.h>
函数名 | 格式 | 功能说明 | 例子 |
---|---|---|---|
最大值 | max(x,y) | 计算x,y的较大值 | max(5,6)=6 |
最小值 | min(x,y) | 计算x,y的较小值 | min(5,6)=5 |
绝对值函数 | fabs(x) | 求一个浮点数的绝对值 | fabs(-5.5) = 5.5 |
绝对值函数 | abs(x) | 求一个整数的绝对值 | abs(-5)=5 |
向下取整 | floor(x) | 求不大于实数x的最大整数 | floor(3.14) = 3 |
向上取整 | ceil(x) | 求不小于实数x的最小整数 | ceil(3.14) = 4 |
平方根函数 | sqrt(x) | 求实数x的平方根 | sqrt(25) = 5 |
四舍五入 | round(x) | 四舍五入到最近的整数 | round(2.5)=3 |
指数函数 | pow(x,y) | 求xy,结果是double | pow(2,3) = 8 |
随机函数 | rand() | 产生0 到 RAND-MAX之间的随机数 | log(1) = 0 |
自然对数函数 | log(x) | 计算x的自然数对数 | |
自然数指数函数 | exp(x) | 求实数x的自然指数e | exp(1)=2.718282 |