【C++入门】缺省参数 | 函数重载

目录

4.缺省参数

4.1缺省参数的概念

4.2缺省参数分类

4.3声明和定义分离(声明使用缺省参数)

4.🐍声明和定义分离到链接

5.函数重载

5.1函数重载的概念

5.2可执行程序的形成步骤

5.3C++支持函数重载的原理—名字修饰(name Mangling)


4.缺省参数

4.1缺省参数的概念

缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时,如果没有指定实参则采用该形参的缺省值,否则使用指定的实参。缺省参数又叫默认参数。

#include<iostream>
using namespace std;

void Func(int a = 0)
{
	cout << a << endl;
}
int main()
{
	Func(); // 没有传参时,使用参数的默认值
	Func(10); // 传参时,使用指定的实参
	return 0;
}

4.2缺省参数分类

  • 全缺省参数
  • 半缺省参数
  • 函数在给半缺省参数,必须是从右往左连续依次给出,不能间隔跳跃。(从第一个开始)
  • 调用函数传参:必须从左到右连续传参,不能跳跃。(从第一个开始)
  • 形式参数是实际参数的一份临时拷贝。
  • 缺省参数不能在函数声明和定义中同时出现,若有声明只能在声明中出现。
  • 缺省值必须是常量或者全局变量。
  • C语言不支持(编译器不支持。
//全缺省参数
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;
}
//给半缺省参数
#include<iostream>
using namespace std;
//半缺省参数
void Func2(int a, int b = 10, int c = 20)
void Func2(int a, int b , int c = 20)
void Func2(int a, int b, int c)
//❌void Func2(int a, int b = 10, int c)
//❌void Func2(int a=10, int b, int c = 20)
{
	cout << "a = " << a ;
	cout << "b = " << b ;
	cout << "c = " << c ;
	cout << endl;
}
//调用传参
#include<iostream>
using namespace std;
//全缺省参数
void Func1(int a = 10, int b = 20, int c = 30)
{
	cout << "a = " << a ;
	cout << "b = " << b ;
	cout << "c = " << c ;
	cout << endl;
}
int main()
{
	Func1(1, 2, 3);
	Func1(1, 2);
	Func1(1);
	Func1();
	//Func1(, 2, );//❌
	return 0;
}

 

4.3声明和定义分离(声明使用缺省参数)

如果声明与定义位置同时出现缺省参数,恰巧两个位置提供的值不同,那编译器就无法确定到底该用那个缺省值❓

在声明处给缺省参数。因为.cpp在预处理阶段会展开头文件.h。会把.h的声明拷贝到.cpp里面。在后面编译阶段,检查语法也不会出错。

 //a.h
 void Func(int a = 10);
 //a.cpp
 void Func(int a = 20)
{
    ///
}
// 注意:如果声明与定义位置同时有缺省参数,
//恰巧两个位置提供的值不同,那编译器就无法确定到底该用那个缺省值。

4.🐍声明和定义分离到链接

tips:

从语法的角度:函数名就是函数的地址。

从底层的角度:函数调用的本质是call  函数(地址)(物理空间是连续的)

地址是函数的地址。函数底层也是一堆指令。也就是函数底层指令的第一条指令的地址。

CPU执行也是执行指令。 

声明和定义分离,编译阶段检查语法call Func(❓)里面是没有地址的,还没有链接。那编译阶段检查语法为什么不会报错。(前面预处理/编译/汇编阶段是各自走各自的)

  • 编译阶段:语法检查(自定义类型/变量/函数 搜索出处)
  • 汇编阶段,编译器就只是搜索找到声明(承诺)
  • 链接阶段,形成了符号表。
  • 编译器去符号表里搜索,找到函数定义(兑现承诺)
  • 编译器把函数定义的地址放到 call Func(07FF7F71E12E4h)
  • 符号表

//"a.cpp"
#include"a.h"
void Func(int a)
{
	cout << a << endl;
}

//"a.h"
#pragma once
#include<iostream>
using namespace std;
void Func(int a =20);

//test.cpp
#include"a.h"
int main()
{
	Func();
	Func(10);
	return 0;
}

在编译阶段没有找到声明:语法错误❌

在链接阶段没有找到定义:链接❌

5.函数重载

自然语言中,一个词可以有多重含义,人们可以通过上下文来判断该词真实的含义,即该词被重载了。函数重载也就是一词多义
比如:以前有一个笑话,国有两个体育项目大家根本不用看,也不用担心。一个是乒乓球,一个是男足。前者是“谁也赢不了!”,后者是“谁也赢不了!” 

5.1函数重载的概念

函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或 类型顺序)不同,常用来处理实现功能类似数据类型不同的问题。

  • C语言不允许同名函数
  • C++语言允许同名函数。
  • 要求:函数名相同,参数不同,构成函数重载。(编译器会根据数据类型自动匹配)
  • 参数不同:
  1. 参数类型不同
  2. 参数个数不同
  3. 参数类型顺序不同
#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;
}

// 2、参数个数不同
void f()
{
	cout << "f()" << endl;
}
void f(int a)
{
	cout << "f(int a)" << endl;
}


// 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()
{
	Add(10, 20);
	Add(10.1, 20.2);
	f();
	f(10);
	f(10, 'a');
	f('a', 10);
	return 0;
}

5.2可执行程序的形成步骤

在C/C++中,一个程序要运行起来,需要经历以下几个阶段:预处理、编译、汇编、链接。

(前面缺省参数的声明和定义分离铺垫过了)

 

  • 1. 实际项目通常是由多个头文件和多个源文件构成,而通过C语言阶段学习的编译链接,我们可以知道,【当前a.cpp中调用了b.cpp中定义的Add函数时】,编译后链接前,a.o的目标文件中没有Add的函数地址,因为Add是在b.cpp中定义的,所以Add的地址在b.o中。那么怎么办呢?
  • 2. 所以链接阶段就是专门处理这种问题,链接器看到a.o调用Add,但是没有Add的地址,就会到b.o的符号表中找Add的地址,然后链接到一起。

  • 3.面对链接函数的地址到括号里:call  函数(函数地址)
  • C语言符号表:函数名 函数地址
  • C++符号表的规则:函数名且包含函数参数类型等  函数地址(那么链接时,面对Add函数,链接器会使用哪个方式去符号表找呢?这里每个编译器都有自己的函数名修饰规则。只要能区分开即可)(下面细讲)

5.3C++支持函数重载的原理—名字修饰(name Mangling)

C语言不支持函数重载?C++如何支持函数重载?

>>>>>>>>>   和前面我们讲到的声明和定义分离到链接中链接步骤(符号表搜索函数地址)

>>>>>>>>>(在符号表中去搜索函数地址)这个步骤非常关键。

C语言没办法支持重载,因为同名函数没办法区分。而C++是通过函数修饰规则来区分,只要参数不同,修饰出来的名字就不一样,就支持了重载。如果两个函数函数名和参数是一样的,返回值不同是不构成重载的,因为调用时编译器没办法区分。


 【C语言】 

C语言在符号表中去搜索函数地址 >>>>只根据函数名搜索函数地址,当然C语言不支持函数重载。C语言链接时,直接用函数名去找地址,有同名函数,区分不开。


 【C++】

祖师爷为了C++能够支持函数重载,于是把搜索规则改变了。C++在符号表去搜索函数地址,规则>>>>>>>>>>>>>>>>>>>函数名且包含函数参数类型等  函数地址,支持函数重载。

这里每个编译器都有自己的函数名修饰规则。函数名修饰规则,名字中引入参数类型,各个编译器有自己的实现一套。(下面从windows和Linux举例)

【windows下名字修饰规则】 

【扩展学习:C/C++函数调用约定和名字修饰规则--有兴趣好奇的可以看看,里面
有对vs下函数名修饰规则讲解】C/C++ 函数调用约定___declspec(dllexport) void test2();-CSDN博客

#include<iostream>
using namespace std;
int Add(int left, int right);
double Add(double left, double right);
int main()
{
	Add(10, 20);
	Add(10.1, 20.2);
}
//可以去VS上只有声明没有定义,此时就会报链接错误❌

【Linux下名字修饰规则】

通过下面我们可以看出gcc的函数修饰后名字不变。而g++的函数修饰后变成【_Z+函数长度+函数名+类型首字母】。

  • 采用C语言编译器编译后结果
  • 结论:在linux下,采用gcc编译完成后,函数名字的修饰没有发生改变。
  • 采用C++编译器编译后结果
  • 结论:在linux下,采用g++编译完成后,函数名字的修饰发生改变,编译器将函数参
    数类型信息添加到修改后的名字中。
      #include<stdio.h>                            
    2 int Add(int a,int b)                 
    3 {                                    
    4     return a+b;                      
    5 }                                    
    6 void func(int a,double b,int* p)     
    7 {                                    
    8                                      
    9 }                                    
   10 int main()                           
   11 {                                    
   12     Add(1,2);                        
   13     func(1,2,0);                     
   14     return 0;                        
   15 }   
  • 采用C语言编译器编译后结果
gcc -o projectC project.c
objdump -S projectC

  • 采用C++编译器编译后结果
g++ -o proejctCPP project.cpp
objdump -S projectCPP(proejctCPP)

对比Linux会发现,windows下vs编译器对函数名字修饰规则相对复杂难懂,但道理都
是类似的,我们就不做细致的研究了。 🙂感谢大家的阅读,若有错误和不足,欢迎指正。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

唐唐思

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值