4.1函数的定义与调用
函数定义
一个函数是需要定义后才能使用
函数定义格式
类型标识符 函数名(形参类型说明表) {
函数体
}
函数的定义包含如下几个部分:
1.函数名:
命名方式与变量名相同,要求符合标识符命名规则
2.函数的参数:
- 有参函数:函数名后定义有变量专门接收外部传送的数据,接受外部数据的变量叫形式参数,外部传送过来的变量叫实际参数
- 无参函数:函数运行不需要从外部传送数据,函数名后的的()不可以省略,括号内可以写void也可一不写
有参函数参数传递注意事项:
- 未运行函数调用语句时,形参不占用内存的存储单元,开始调用时才占用,调用结束,形参所占内存单元被释放
- 实参对形参是“值传递”,即单项传递,实参形参类型一致调用时一一对应
- 实参可以是常量、变量、表达式,表达式的话先计算其值再调用
- 形参一定要一一定义其类型
3.函数值的类型
又称函数的类型,函数运行后有一个确定的值,这个值的类型就是函数的类型。用函数定义中的类型标识符来描述。可以是int、double、float等
注意:
- 函数运行后确定的值只能用return语句得到
- 函数的类型和return表达式的类型不一样,以函数的类型为准,return表达式值自动转换
- 函数体中允许多个return语句,但每次调用只允许一个return语句被执行,函数只能返回一个值
- 部分函数只完成操作,不需要函数值,这种函数类型称为空类型,定义为void类型
函数调用
1.函数调用的格式:
函数名(实参表);
2.函数调用的方式:
- 函数语句
- 函数表达式
- 函数参数
3.函数的原型声明
- 被调函数必须是存在的函数
- 若使用系统已定义的函数,应包含其库文件
- 被调函数遵循先定义后调用
- 用户自己定义的函数,位于主调函数之后,要写原型声明
原型声明形式:函数类型标识符 函数名(形参类型说明表);
函数原型声明之前可以省略形参变量名,只给出形参类型
引用作为函数参数
本质是给已有的变量起一个别名,系统不会为引用变量分配内存空间
类型标识符&引用变量名=变量名
int i;
int&r=i;int i,&r=i;
注意:
- 引用要在定义的时候初始化,且初始化值不可以是常数。 int&r;int &r=5;
- 不能定义引用的引用。int&r=&m;
- 若定义时使用const,表示禁止通过引用来修改变量的值。const int&r=i;
#include <iostream>
using namespace std;
void swap(float&x,float&y) {float t;t=x;x=y;y=t;}
函数可以递归或嵌套调用
4.2内联函数
对于函数体较短小、功能较简单的函数,若被频繁调用会花去大量时间,所以产生内联函数机制,函数体代码直接插入到函数调用处。
定义内联函数只需要再定义函数时前面加一个关键字inline修饰即可。
注意:C++中,处函数体内含循环,switch分支,复杂嵌套的if语句之外,其余函数均可定义为内联函数
4.3函数重载
函数重载是指完成不同功能的函数使用相同的函数名
重载函数的说明:
- 定义的重载函数必须具有不同的参数个数或不同的参数类型
- 仅返回值不同时,不能定义重载函数
int sum(int a,int b) {return a+b;
}
double sum(int a,int b){return(a+b)*1.0;
}
4.4带有默认参数的函数
C++在函数声明或定义时对参数预赋一个或多个默认值。在函数调用时可以给出实参也可以不给实参
#include<iostream>
using namespace std;
int area(int lng=4,int width=2) {return lng*width;
}
int main() {int a=8,b=6;cout<<area(a,b)<<endl; //area(8,6)cout<<area(a)<<'\n'; //area(8,2)cout<<area()<<endl; //area(4,2)return 0;
}
注意:
- 默认参数个数不限,但所有的默认参数必须放在参数表的最右端,即先定义所有的非默认参数,再定义默认参数
int area(int lng,int width=2)
不可写成:
int area(int lng=4,int width)
- 若既有函数原型声明,又有函数定义,默认参数必须再函数原型声明中给出,不能在函数定义中给出,函数原型声明中的变量名可以省略。默认参数只能赋值一次。
4.5局部变量和全局变量
局部变量
也称内部变量,指在函数体中定义的变量,作用域只在本函数范围内
注意:
- 主函数中定义的变量只能在主函数中使用
- 形参变量属于被调函数的局部变量
- 允许在不同函数中使用相同的变量名
- 复合语句中也可以定义变量,属于局部变量,作用域只在复合语句范围内,当复合语句出现与外部同名变量时,在复合语句内则复合语句定义的变量起作用,换句话说,变量名相同,作用域小的变量先使用
全局变量
也称外部变量,是函数外部定义的变量,作用域是整个源程序
注意:
- 全局变量定义后若不赋初值,编译时自动赋值为0
- 全局变量增加了函数间数据联系的通道,利用全局变量可以减少函数间参数的个数,减少内存空间的使用及传递数据的时间消耗
- 全局变量所有函数共有,滥用会破坏程序的模块化结构,因此尽量少用全局变量
- 若同一源文件中全局变量与局部变量同名,则局部变量的作用范围内全局变量不起作用。
- 若在函数中要使用与其局部变量同名的全局变量,使用作用域运算符“::”来限定全局变量
4.6变量的存储类别
变量的生存期和存储类别
变量的生存期是变量占用存储单元的时间
程序在内存占用的存储空间分为三部分:
- 程序区:存放可执行程序代码
- 动态存储区:用于存放auto类型的局部变量、函数的形式参数、函数调用时的保护现场和返回地址等数据。函数调用时分配空间,结束时释放空间
- 静态存储区:用于存放全局变量和局部静态变量
针对某一变量其分配到静态存储区还是动态存储区由定义变量类型时的存储类型确定
变量的存储类型:自动(auto)型、静态(static)型、寄存器(register)类型、外部(extern)型。
auto型变量
定义局部变量时,用auto修饰的变量,属于动态变量,开始调用时分配空间,结束时回收,C++默认局部变量时auto型,所以当定义局部变量时,一般不用关键字auto修饰。
注意:对于自动类型变量,若没有明确的赋初值,其初值是不确定的。
static局部变量
静态局部变量,程序在开始时为其分配空间,调用结束后,系统并不会回收这些变量所占用的存储空间,再次调用时变量任然使用原来分配的存储空间,因此变量任然保留上一次函数调用结束时的值。
include<iostream>
using namespace std;
int fun(int a) {static int b=3;b=b+a;return b;
}
void main() {int a=2;cout<<"第一次调用"<<fun(a)<<'\n'; //5cout<<"第二次调用"<<fun(a)<<'\n'; //7
}
注意:
- 静态局部变量在赋初值时,只赋值一次,程序运行时已有初值,每次调用时不再重新赋值,而是使用上一次的值
- 定义静态局部变量,若没有赋初值,自动赋值0或空字符'\0'
- 静态局部变量在调用结束时仍存在,由于变量作用域限制,其他函数无法引用
register型变量
将局部变量的值放在cpu寄存器中,用关键字register作声明,采用动态存储方式
register int i,j;
注意:
- 仅局部自动变量和形式参数可以作为寄存器型变量
- 寄存器数目有限,不能定义多个寄存器变量
- 静态局部不能定义为寄存器变量
extern型变量
外部类型变量,一定是全局变量。用extern修饰全局变量,作用是拓展全局变量的作用域
- 在同一个源文件中修饰全局变量:若全局变量不在开头定义,想要引用可以用extern修饰,表明该全局变量已经存在
- 多文件组成的程序中修饰全局变量:将全局变量的作用域拓展到文件中
static声明全局变量
表示所定义的全局变量仅限这个源程序文件使用,不能被其他文件引用
4.7内部函数和外部函数
内部函数:限定再本源文件程序使用。定义函数时在zai类型标识符前加上static。
外部函数:多个源文件程序中,一个函数不仅能在本源程序中使用还可以在其他源程序中使用,定义函数时在zai类型标识符前加上extern,若省略extern,默认为外部函数。
注意:多个源文件组成的程序,在一个文件中调用另一个文件的外部函数,必须在调用文件中对被调的外部函数作声明,并在声明前加上extern