导图社区 【 Cxx程序设计教程】第四章 函数
这是一篇关于【 Cxx程序设计教程】第四章 函数的思维导图,主要内容有函数的定义和调用、函数的参数传递、递归函数、内联函数(inline)等。
编辑于2022-12-25 17:19:16 广东Ch4 函数
函数的定义和调用
分类
系统函数(可直接使用)
用户自定义函数
函数的定义
函数功能的实现:函数头部+函数体
两种类型
有返回值的函数
更加接近数学的函数
函数类型 函数名(形式参数表) { 声明部分; 语句部分; return 表达式; }
例:求矩形面积的函数
注意
每个形参前面都要加上类型描述
函数不需要参数则形参表为空写成()或(viod)
return表达式要与函数类型一致,否则系统会自动转换(函数定义时的类型),如若转换失败则显示编译出错
没有返回值的函数
相当于一个过程
void 函数名(形参表) { 声明部分; 语句部分; }
eg:函数print的功能是输出n个*号
void print(int n) { for(int i=1;i<=n;i++) cout<<'*'; cout<<endl; }
注意
函数不允许嵌套定义
所有函数在定义时是互相独立的,不存在包含或者从属关系
eg:将add函数的定义放到main函数里面,编译时将产生错误
函数的调用
两种形式
函数表达式
函数名(实参表)
eg:cout<<area(1,2)[输出长为1宽为2的矩形面积]
函数语句
函数名(实参表);
print(5);[输出5个*]
没有返回值的函数只能用这种形式
实际参数与形式参数要保持一致:参数的个数、参数的类型
函数的声明
把函数的名字、类型以及形参的个数、类型和顺序通知编译系统
声明→调用→定义
定义→调用
形式
函数类型 函数名(参数类型1, 参数类型2…);
即:“函数头”末尾加";"
编译系统只关心函数的参数个数、类型、顺序
使用函数的步骤
方式一
函数少,篇幅小
函数定义→函数调用
方式二
函数使用多,篇幅大
函数原型声明→函数调用→函数定义
int add(int, int); --------------------------//函数声明 int main() { int a=1,b=2; cout<<a<<'+'<<b<<'='<<add(a,b); } int add(int x, int y) { return x+y; }
函数的参数传递
形参与实参
形参:定义函数时的参数
实参:调用函数时的参数
关系
只有在函数调用时,才给形参被分配内存空间
调用结束,形参空间被释放
实参可以是变量、常量表达式
实参与形参的类型应相同或赋值兼容
若不一样,在传递过程中实参会自动转成形参的类型
若两类型不兼容,函数调用出错
参数的传递
函数调用时,主调函数与被调函数之间通过参数传递信息
实参与形参的结合方式
单向参数传递——值传递
实参可以是变量名、表达式
实参的值复制给形参,此后实参与形参脱离关系
在函数调用过程中对形参的任何改变都不会影响实参的值
双向参数传递
引用参数传递
C++新增 “引用”比“指针”更 简便
在函数定义时,将形参声明为实参的引用
定义函数时在需要需要声明的形参前加上“&”
void swap(int &x, int &y)
在函数调用时,形参与实参指的是同一个变量,因而被调函数的执行可能改变实参
PS:实参不能是常量或表达式
地址参数传递
实参地址(指针或数组名等)传给形参,通过执行被调函数中对形参的间接运算,从而改变实参的值
函数定义时,形参前加上“ * ”
void swap(int *px, int *py)
函数调用时,对用实参前加上“&”
swap(&a, &b);
数组和指针作为函数参数
数组作为函数参数
将数组的首地址传递给函数
形参数组的改变将影响对应的实参数组
指针作为函数参数
地址传递
形参指向的内容的改变将影响实参指向的存储单元的内容
递归函数
函数嵌套调用
定义函数时不能嵌套
被调用的函数又调用另一个函数
函数的递归调用
一个函数调用它自身
函数直接调用自身
定义函数A时,函数体里由有调用函数A的语句
函数间接调用自身
比如,调用关系是:A--B--C--A,函数A间接调用它自身
注意
不能让递归无限地进行下去
使递归调用结束的条件称为递归初始边界
达到初始边界时,问题已简单了,可直接得到结果
选择递归or非递归方法
追求效率,可选择非递归方法,因为实现递归时的时空开销大;
追求简明,可考虑递归方法
有些问题只能用递归方法,如:Hanoi塔问题
计算整数n的阶乘n!
n!的递归定义: n!=1 当n=1........(1) n!=n.(n-1)! 当n>1时......(2)
内联函数(inline)
以空间换时间
内联函数被执行时,不涉及流程的转出和返回,以及参数传递
原因:编译时已将内联函数的代码直接嵌入到主调函数中,提高了执行效率
内联函数的声明
在函数声明或定义时,在函数名前面加inline
举例理解
编写一个内联函数max,用于求三个数中的最大值
inline int max(int x, int y, int z) {if(y>x) x=y; if(z>x) x=z; return x; }
如果调用函数w=max(a,b,c),编译系统将用max函数体的代码代替max(a,b,c),同时将实参代替形参
w=max(a,b,c)被解释为
f(b>a) a=b; if(c>a) a=c; w=a;
应用
用于规模小、调用频繁的函数
内联函数中不能含循环语句、switch语句以及递归函数(复杂的控制函数)
将函数声明为inline函数只是对编译系统的建议
编译系统根据情况决定是否将函数作为内联函数处理
形参含有默认值(缺省值)
针对调用某个函数时实参总是相同
C++允许函数形参带有默认值(缺省值),以备函数调用时不指定实参值
使用
在函数最早出现位置指定参数默认值
举例理解
函数声明 int add(int x=1,int y=2);
函数调用add( );相当于add(1,2);
函数调用add(5);相当于add(5,2);
默认值的设定
可以给函数的形参提供默认值,也可以不提供,还可以只对形参的一部分给出默认值
形参指定默认值的约定
如果一个形参带有默认值,那么该形参右面的所有形参都必须有默认值,否则出错
举例理解
计算长方体的体积的函数
int V(int a=1, int b=1, int c=1); //正确 int V(int a, int b=1, int c=1); //正确 int V(int a, int b, int c=1); //正确 int V(int a=1, int b, int c=1); //错误 int V(int a, int b=1, int c); //错误 int V(int a=1, int b=1, int c); //错误
函数重载
处理功能相似但参数不同的函数
定义
定义多个相同名称的函数,但这些函数的形参不同(类型、个数、顺序)
使同一个函数名对应不同的实现
编译系统在函数调用时能够将各个函数区分开来
举例说明
编写重载函数perimeter,用于求长方形、三角形和圆的周长
函数定义
const double PI=3.14159; double Perimeter(double a,double b) { return (a+b)*2; } double Perimeter(double a,double b,double c) { return (a+b+c); } double Perimeter(double r) { return 2*PI*r; }
函数调用
根据实际参数的个数是1个、2个还是3个,分别调用圆、长方形、三角形的周长函数
int main() { cout<<Perimeter(3.0,2.0)<<endl;//求长和宽分别为3和2的矩形周长 cout<<Perimeter(3,4,5)<<endl;//求三个边分别为3、4、5的三角形周长 cout<<Perimeter(10.0)<<endl;// 求半径为10的圆周长 }
匹配函数重载的规则
首先尝试完全匹配(参数的个数、类型、顺序一致)
尝试通过参数类型的隐式转换寻求所匹配的函数。例如,int型实参自动转换为double型实参
在调用函数时,强制转换实参类型,使得函数重载匹配成功
注意事项
函数的返回值类型不能作为函数重载的区分条件
两个函数的名字一样,参数的个数、顺序、类型一样,只是返回值不同,编译系统会认为这两个函数是重复定义的错误
不要把功能不同的函数放在一起重载,否则破坏程序的可理解性
函数重载与函数的形参带默认值最好不要同时使用,否则易引发歧义
系统函数
必须用include指令将相关的头文件嵌入到程序中
尖括号用于包含系统函数
#include<iostream>
双引号用于用户自定义头文件
#include "my_head.h"
将头文件嵌入到程序中的两种方式
尖括号头文件到系统目录中寻找,如果找不到,提示出错
双引号头文件先在用户当前目录中寻找,若找不到,再到系统目录中查找
常用的系统函数
数学函数、字符串处理函数、时间和日期函数、输入输出函数等
数学函数(#include<cmath>)
幂函数
pow(3,4)
3的4次方
绝对值
fabs(-5.8)【double型】
|-5.8|
abs(5)【int型】
|5|
开方
sqrt(2)
三角函数
sin(PI/6)
对数函数
log(2)
ln2
随机数函数
伪随机数函数
(#include<cstdlib>)
rand()
产生0~RAND_MAX的随机数
指定随机数范围
0~1
(double)rand()%RAND_MAX
0~n-1
rand()%n
1~n
1+rand()%n
随机数中子函数
(#include<ctime>)
srand(time(0));
产生随时间变化的种子
字符串处理函数(#include<cstring>)
strcpy(s1,s2)
字符串拷贝,把字符串s2复制一份给字符串s1
strcat(s1,s2)
把字符串s2拼接到字符串s1后
Ps:第一个参数(s1)必须保证有足够的存储空间容纳最终字符串,否则运行错误
strlen(s)
获得字符串的长度(单位字节)(不包括末尾的“\0”)
区分:sizeof(s1)字符串占内存空间的大小
strcmp(s1,s2)
比较字符串
s1>s2
返回值为正
s1<s2
返回值为负
两个字符串自左向右逐个字符相比(按ASCII值大小相比较),直到出现不同的字符或遇'\0'为止)
atoi(s1)
字符串转换为整数
变量的作用域与生存期
从空间的反复为讨论变量的属性
作用域和可见域
作用域
标识符的作用范围
从小到大的分类
函数原型作用域
在函数声明中的形参的作用域
形参的有效范围仅限于在形参表内
块作用域
块
一对花括号内的复合语句
选择语句或循环语句构成的一段程序
在一个块内声明的变量,其作用域从声明点开始,到该块结束为止
举例说明
在最后输出时加上对“t”的输出,就会出错(显示t没有定义)
for循环中变量的作用域
for(int i=1;1<100;i++)
循环语句后面的程序还想使用i的时候就会出错
int i=1; for(i;1<100;i++)
循环语句后面的程序还可以使用i
类作用域
文件作用域
在函数之外或者类之外声明的标识符的作用域
从声明的位置开始,一直到所在文件的末尾为止
可见域
尽管标识符在其作用域内都是有效的,但未必可以实际使用
在某些情况下,标识符可能被屏蔽,不可见,不能用
屏蔽原理
内层的变量覆盖同名的外层变量
作用域运算符
::
在局部变量的作用域内使用同名的全局变量,在该变量前加上的符号
举例说明
#include<iostream.h> int k=1; //语句1 int main() //语句2 { int k=2; //语句3 { int k=3; //语句4 cout<<k<<endl; //语句5 cout<<::k<<endl; //语句6 } //语句7 cout<<k<<endl; //语句8 return 0; //语句9 } //语句10
语句6输出的是全局变量k()
作用域是变量理论上有效的区域 可见域是变量实际有效的区域
局部变量和全局变量
局部变量
在函数内定义的变量
包括
块内定义的变量
函数内定义的变量
类内定义的变量
特点
仅在定义它的函数内才能有效使用
从变量定义的位置开始,到函数体结束
编译器不为局部变量分配内存单元,而是在程序运行当中
当局部变量所在的函数被调用时,分配内存
当函数执行结束时,局部变量占用内存被收回
全局变量
在函数外定义的变量
特点
编译器为全局变量分配内存,在程序运行期间始终有效,直到程序运行结束
全局变量可被其作用域中的所有函数访问
提供了不同函数间联系的另一途径
不只局限于参数传递和return语句
变量的生存期与存储类型
变量生存期
变量占用内存时间
存储类型
作用
存储类型决定变量的生存期长短
影响变量初始值设定
C++的存储空间分为三种
程序区
存放程序代码
静态存储区
存放全局变量和静态变量
在编译时为它们分配存储空间,程序结束时释放这些空间
动态存储区
存放局部变量和函数形参等
在函数开始执行时被分配存储空间,函数结束时释放空间
数据存放
变量的存储类型 (两种)
动态存储类型
auto(自动)
不加修饰的局部变量均视为自动变量
register(寄存器)
编译程序在可能情况下,可把一些局部变量放在寄存器中,以提高访问效率
寄存器有限的情况下,若编译程序无法为寄存器变量分配给相应寄存器时,自动把它当作自动变量
格式
register 类型 变量名;
静态存储类型
extern(外部)
外部变量是指能被其他文件使用的变量
没有用static修饰的全局变量是外部变量
声明
定义性声明 (仅1次)
一个全局变量在声明时未指定存储类别,则默认存储类别为外部
若此时未指定初值,则默认初始化为0
作为定义性声明的extern变量必须是全局变量
指示编译程序要为该变量分配内存单元
引用性声明 (多次)
表明变量在其他地方已经定义
可以是全局性变量、局部变量
告诉编译程序该名字的变量存放在程序的其他地方已经给出了定义性声明这里只需要引用其文字不必为其分配内存单元
两种声明可以同时出现在同一个文件作用域中
extern int age=30;//定义性声明 extern int age; //引用性声明 int age; //如此声明全局变量视作外部变量
用途
在一个源程序中,使用另一个源程序中的全局变量
static(静态)
格式
static 类型 变量名;
静态变量的缺省初始化值为0
按作用范围分
静态局部变量
具有全局寿命
从程序启动开始到程序运行结束
作用域是局部的
初始化只做一次,即在程序运行中第一次经过它的声明时完成; 以后再次遇到声明时则采用上一次运行的结果作为当前值
静态全局变量
具有全局寿命
作用域限于所在文件内,其它文件不能使用
初始化在执行函数main()之前完成