一、缺省参数
1. 缺省参数的定义
缺省参数是声明或定义函数时为函数的参数指定一个默认值。在调用该函数时,如果没有指定实参则采用该默认值,否则使用指定的实参。
举例说明:
void Func(int a = 0)
{
cout<<a<<endl;
}
int main()
{
Func(); // 没有传参时,使用参数的默认值
Func(1); // 传参时,使用指定的实参
}
缺省参数有两个重要性质:
①缺省值必须是常量或者全局变量
int m = 0; //全局变量
void Func(int a = m)
{
cout<<a<<endl;
}
int main()
{
Func(); // 没有传参时,使用参数的默认值
Func(1); // 传参时,使用指定的实参
}
②缺省参数不能在函数声明和定义中同时出现
//a.h
void TestFunc(int a = 10);
// a.c
void TestFunc(int a = 20)
{}
// 会报错
2. 缺省参数的分类
主要分为全缺省参数和半缺省参数
2.1 全缺省参数
顾名思义,即声明或定义函数时,函数的所有参数全为缺省参数。
调用函数时可以指定部分实参,但必须是连续的。
*****全缺省******
void Func(int a = 10 , int b = 20 , int c = 30)
{
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << endl <<endl;
}
int main()
{
Func();
Func(1);
Func(1,2);
Func(1,2,3);
return 0;
}
2.2 半缺省参数
半缺省参数,即函数的部分参数为缺省参数,但必须是从右到左依次给出,不能间隔
******半缺省******
void Func(int a , int b = 20 , int c = 30)
{
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << endl <<endl;
}
int main()
{
Func(1);
Func(1,2);
Func(1, 2,3);
return 0;
}
二、函数重载
1. 函数重载的定义
函数重载是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或 顺序)必须不同,常用来处理实现功能类似数据类型不同的问题
1. 参数个数不同
int Add(int left, int right)
{
return left + right;
}
int Add(int left, int right , int a)
{
return left + right;
}
2. 参数类型不同
int Add(int left, int right)
{
return left + right;
}
double Add(double left, double right)
{
return left + right;
}
3. 参数顺序不同
int Add(int left, int right)
{
return left + right;
}
int Add(int right, int left)
{
return left + right;
}
注意!!!函数重载只能C++下支持,C语言不支持函数重载,具体原因涉及到名字修饰了
2. 名字修饰
在C/C++中,一个程序要运行起来,需要经历以下几个阶段:预处理、编译、汇编、链接。
- 预处理:头文件展开、宏替换、条件编译、去掉注释
- 编译:语法分析,词法分析,语义分析,符号汇总,生成汇编代码
- 汇编:形成符号表、汇编代码转换成二进制机器码
- 链接:合并段表、符号表的合并与符号表的重定位
先看一个程序,该程序定义了两个源文件,一个头文件,将函数的声明和定义分开了
在VS环境可以在程序运行后通过反汇编查看程序的汇编代码,可以更加深入了解函数调用过程
运行后函数调用的汇编代码:
按F11继续运行
继续按F11
可以看出,调用一个函数,在汇编代码中是 call 函数名(地址) 地址为jmp指令的地址,即call指令跳转到jmp指令后,再跳转到函数地址。
即Call 到函数跳转表 再jmp到函数起始位置
call后面的地址是什么时候放进去的呢???
1. 如果在当前文件中有函数的定义,则知道函数的地址,编译时就填上地址了
2. 如果在当前文件中只有函数的声明(如上述所举的例子),函数定义在其他源文件中,编译时便不知道函数的地址,所以不知道call 后面要放入什么地址,只能在链接时处理,链接器看到test.o调用 f(),但是没有 f() 的地址,就会到func.o的符号表中找 f() 的地址,然后链接到一起去
那么这个地址是如何放进去的呢???
汇编产生.0文件的同时会形成符号表 ,在链接时,链接器会根据函数名/函数名修饰后的名字去符号表中寻找地址,每个编译器有自己的函数名修饰规则。
由于Windows下vs的修饰规则过于复杂,而Linux下gcc的修饰规则简单易懂,下面我们使用了gcc演示了这个修饰后的名字
1. 采用C语言编译器编译后的结果
在linux下,采用gcc编译完成后,函数名字的修饰没有发生改变。
2. 采用C++编译器编译后的结果
g++的函数修饰后变成【_Z+函数长度+函数名+类型首字母】
由此可以得出结论:
1、C语言不支持函数重载,因为编译的时候,两个重载函数函数名相同,而符号表中存放的是函数名和地址的映射,而符号表中不能同名,因此在func.0符号表中存在歧义和冲突,其次在链接的时候也存在歧义和冲突
1、C++中不是直接用函数名来标识和查找函数,有了函数名修饰规则,只要参数不用,.o文件符号表里面重载函数就不存在冲突了