C++入门知识(二)

一、缺省参数

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文件符号表里面重载函数就不存在冲突了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值