五、缺省参数
(一) 缺省参数 概念
缺省参数是 声明或定义函数时 为函数的 参数指定一个缺省值 。
在调用该函数时,如果没有指定实参则采用该形参的缺省值,否则使用指定的实参 。
void Func(int a = 0) { cout<<a<<endl; } int main() { Func(); // 没有传参时,使用参数的默认值 Func(10); // 传参时,使用指定的实参 return 0; }
(二) 缺省参数分类
- 全缺省参数
void Func(int a = 10, int b = 20, int c = 30) { cout<<"a = "<<a<<endl; cout<<"b = "<<b<<endl; cout<<"c = "<<c<<endl; }
- 半缺省参数
半缺省参数必须 从右往左 依次来给出,不能间隔着给
void Func(int a, int b = 10, int c = 20) { cout<<"a = "<<a<<endl; cout<<"b = "<<b<<endl; cout<<"c = "<<c<<endl; }
注意:
- 缺省参数不能在 函数声明 和 定义 中同时出现
如果 声明与定义位置同时出现 ,恰巧两个位置提供的值不同,那编译器就无法确定到底该用那个缺省值。
//a.h void Func(int a = 10); // a.cpp void Func(int a = 20) {}
- 缺省值必须是 常量 或者 全局变量
- C语言不支持(编译器不支持)
六、函数重载
C不支持同名函数,C都是靠函数名来识别函数的,但功能类似,只是因为 形参列表 (参数个数 或 类型 或 类型顺序)不同 ,而因此要开辟很多不同的函数名。这是一件很麻烦且效率低下的事情。
对此,C++做出改进,提出了函数重载的概念。
(一) 函数重载概念
函数重载:是函数的一种特殊情况,C++允许在 同一作用域中 声明几个功能类似的 同名函数,这
些同名函数的 形参列表(参数个数 或 类型 或 类型顺序)不同 ,常用来处理实现 功能类似数据类型不同 的问题。
- [ 返回值可用可不用 ]
会出现不知道该调用谁。
(二)函数重载
(1)参数类型不同
#include<iostream> using namespace std; // 1、参数类型不同 int Add(int left, int right) { cout << "int Add(int left, int right)" << endl; return left + right; } double Add(double left, double right) { cout << "double Add(double left, double right)" << endl; return left + right; } int main(){ Add(10, 20); Add(10.1, 20.2); }
(2)参数个数不同
// 2、参数个数不同 void f() { cout << "f()" << endl; } void f(int a) { cout << "f(int a)" << endl; } int main() { f(); ☆//f()则不知道该调用 f()还是f(int a) f(10); //f(10)可以很明确的是传给f(int a); }
(3)参数类型顺序不同
// 3、参数类型顺序不同 void f(int a, char b) { cout << "f(int a,char b)" << endl; } void f(char b, int a) { cout << "f(char b, int a)" << endl; } int main() { f(10, 'a'); f('a', 10); return 0; }
(三)函数重载 底层原理_名字修饰(name Mangling)
为什么C++支持函数重载,而C语言不支持函数重载呢?
在C/C++中,一个程序要运行起来,需要经历以下几个阶段:预处理、编译、汇编、链接。
- 预处理(
.i
文件)
预处理指令,头文件展开
- 编译(
.s
文件)
语法分析,词法分析,语义分析,符号汇总
(语法束)检查语法,生成汇编代码 [指令级代码]( 给我们看的 )
2.1 函数调用在编译阶段,call(地址)
[ 只有声明 ( 但编译器让它过 ),没有地址 ]
[会 在链接阶段,通过根据汇编阶段生成的.o
文件符号表( 找到对应函数名的地址 )将call()里面的地址进行链接 ]
2.2 并且将参数带入进行修饰产生 [ 函数名修饰规则 ]
由于Windows下vs的修饰规则过于复杂,而Linux下g++的修饰规则简单易懂,下面我们使用了g++演示了这个修饰后的名字。
通过下面我们可以看出 gcc(C)的函数修饰后名字不变。而 g++(C++)的函数修饰后变成【_Z+函数长度+函数名+类型首字母】
- 采用C语言编译器编译后结果
结论:在linux下,采用gcc编译完成后,函数名字的修饰没有发生改变。 - 采用C++编译器编译后结果
结论:在linux下,采用g++编译完成后,函数名字的修饰发生改变,编译器将函数参数类型信息添加到修改后的名字中。 - Windows下名字修饰规则
对比Linux会发现,windows下vs编译器对函数名字修饰规则相对复杂难懂,但道理都是类似的,我们就不做细致的研究了
【扩展学习:C/C++函数调用约定和名字修饰规则–有兴趣好奇的同学可以看看,里面有对vs下函数名修饰规则讲解】
通过这里就理解了 C语言没办法支持重载,因为同名函数没办法区分。而C++是通过函数修饰规则来区分,只要参数不同,修饰出来的名字就不一样,就支持了重载。
如果两个函数 函数名和参数是一样 的,返回值不同 是 不构成重载 的,因为调用时编译器没办法区分。
返回值可用可不用 ,会出现不知道该调用谁的情况。
- 汇编( 可重定位目标文件
.o
文件)
3.1 形成符号表。(汇编调用函数时,才会call 地址 ,因此符号表就是在这时产生的)
符号表:
形式 “ 函数名 地址 ” [ 函数名 和 地址 的映射 ]
3.2 汇编指令 -> 二进制指令 [ 转换成二进制的机器码( CPU 能认识的 )] -> test.o
- 链接
合并段表
符号表的合并 和 符号表的重定位
合并到一起,链接一些没有确定的函数地址等 [ 用函数名去找地址( 函数名修饰规则 )]
实际项目通常是由多个头文件和多个源文件构成,而通过C语言阶段学习的编译链接,我们可以知道,【当前a.cpp中调用了b.cpp中定义的Add函数时】,编译后链接前,a.o的目标文件中没有Add的函数地址,因为Add是在b.cpp中定义的,所以Add的地址在b.o中。那么怎么办呢?
所以 链接阶段就是专门处理这种问题,链接器看到a.o调用Add,但是没有Add的地址,就会到b.o的符号表中找Add的地址,然后链接到一起。
那么链接时,面对Add函数,链接接器会使用哪个名字去找呢?这里就通过我们前面 编译阶段讲的 每个编译器都有自己的 函数名修饰规则来找。