第5章 函数
5.1函数的性质
1 a)函数可以有参数有返回值,可以没参数有返回值,可以有参数没有返回值,也可以无参数无返回值,表现的像一个过程。
b)函数具有独立性,一个函数内不能定义另一个函数,所以不存在子函数的概念。
2 函数参数靠传值机制实现,传值即在函数被调用时,用克隆实参的办法来创建形参,而实参本身不发生改变。传值时,实参和形参类型应该匹配或者能隐式类型转换。
void f2(int b){};
void f3(int* c){};
void f4(int& d){};
int main()
{
f2(2.5);
f2('c');
int e = 3;
// f3(e); //error C2664: 'f3' : cannot convert parameter 1 from 'int' to 'int *'
f3(&e);
f4(e);
// f4(&e); //error C2664: 'f4' : cannot convert parameter 1 from 'int *' to 'int &'
return 0;
}
3 运行时内存
代码区 | 程序代码 |
全局数据区 | 全局数据、常量、文字量、静态全局量、静态局部量 |
堆 | 动态内存 |
栈 | 函数数据区(局部数据区) |
4 局部数据的不确定性
原因:os为函数分配的栈空间当被使用再回收之后,并没有被清扫,当再次非配给局部变量时,其中的数值是不确定的。
5函数指针
(1)int *gp(int); //声明一个指针函数
int (*gp)(int); //定义一个函数指针,其类型为:int (*) (int)
(1)函数指针的初始化方式:
A. int g(int); int (*gp)(int) = g; //定义时初始化 B. int (*gp)(int); gp = g; 先定义后赋值。
(2)声明一个函数指针类型
Typedef int (*Fun)(int a,int b); int m(int,int); Fun funp = m;
(3)typedef int Func(); // 将函数类型称为Func
此时函数指针可以定义为: Func * pf;
int (*func(int)) (); // 为一个函数声明,声明*func(int) 为int ()型,等同于: Func * func(int);
它是返回一个函数指针的带int 型参数的函数声明,其返回的函数指针类型为int().
Typedef int (* SIG)();
Typedef int(* SIGARG)(); //声明函数指针
SIG signal(int , SISGARG);//声明的函数返回函数指针,其参数为整型和函数指针
(1) 如果函数参数中出现了函数类型,则自动转换为函数指针,这种现象叫做蜕变(decay).
(2)
如:MenuFun fun[] = {f1,f2}; 即是蜕变的形式,本应为MenuFun fun[] = {&f1,&f2};
其函数调用的形式为fun[0](),本应为*fun[0]();
同理可以使用函数引用 :
Void g(); typedef void Fun(); Fun & f=g; f();//????
(3) 函数指针的作用:
a) 调用其他语言的函数或过程
b) 通过动态链接库的形式访问其他的进程
c) 函数指针作为参数,当参数取不同的值时,可以使函数表现出不同的形式。
6 命令行参数
使用标准输入输出设备的程序可以重定向输入输出方式。
可重定向的程序的的好处是可以在运行时临时决定输入输出的方式。
不专门指定设备时默认为标准输入输出,否则‘<’后面规定输入设备,‘>’后面规定输出设备。‘》’以追加的形式输出到输出设备。
#include<iostream>
using namespace std;
int main()
{
for(int a, int b; cin>>a>>b; cout<<a+b<<endl);
return 0;
}
默认为标准输入输出。
在命令行输入命令:
D:/> redirect <abc.txt >xyz.txt
表示运行redirect程序时的输入设备为abc.txt ,输出设备为xyz.txt
D:/> redirect <abc.txt >>xyz.txt 表示以追加的方式输出到xyz.txt
7 使用main函数
当程序要处理的是若干个非标准设备的资源文件,或者遵循某种形式的命令时,可以使用main函数的参数。
Int main(int argc, char** argv)
其中argc 为参数的个数,argv为参数数组。argv 为一个c串数组,类型为char** ,数组的最后一项为空串。
Argv 的出现形式可以是:程序待处理的字符、输入输出文件名、命令等。
注意: 所运行的程序的名称也是argv中的一个参数。
8 递归函数
(1)递归条件:
A.有停止条件,即递归函数中有完成终极任务的语句序列。Eg.if(n==1) return 1;
B. 递归条件应该先测试,后递归调用。
(2)递归评说
递归的好处的是便于设计程序、简单易读,不利之处是它增加了系统的时间、空间开销:在时间上,执行函数的调用和返回要大于非递归函数;在空间上没递归一次便增加一段栈开销,影响程序性能。
9函数重载
(1) C++在调用重载函数时的顺序是:1)寻找严格匹配2)通过相容 类型的隐式转换匹配3)通过用户定义的转换匹配。
(2) void print(long a){cout<<a<<endl;}
void print(double a){cout<<a<<endl;}
print(a); //error C2668: 'print' : ambiguous call to overloaded function
(3) c++函数重载技术的实现:利用函数名压轧技术来改变函数名,区分参数不同的同名函数。例如 void fun(char a,int b,double c); 被压轧为fun_cid或fun_c_i_d(不同编译器实现不同)。
10默认参数
(1)默认规则:
a)默认参数只能在函数声明或定义中出现一次,不能在两者中同时出现。通常情况下,默认参数应放在声明中。
b)默认参数只能从后往前设置
eg.void func(int a=1, int b, int c=3, int d=4); // missing default parameter for parameter 2
void func(int a, int b=2, int c=3, int d=4); //ok
默认参数的替换也只能从后往前逐个替换尾部的“缺陷”
Func(); //error
Func(12,12); //ok, c、d默认
Func(2,15, , 20);//error,d不默认,c也不能默认
11 重载还是参数默认
A) 如果两个函数做基本相同的事,只不过参数个数不同,则适合于用参数默认
Eg. Void decay(); void decay(int a );
可以将两者重载,也可以先定义后者,再重载decay():
Void decay(){decay(2);},最好的方法是利用参数默认:void decay(int a = 2){}.
B) 如果一个参数的值用来确定不同的操作,则适合用重载。