【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.hvoid Func(int a = 10);//a.cppvoid 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编译器对函数名字修饰规则相对复杂难懂,但道理都
是类似的,我们就不做细致的研究了。 🙂感谢大家的阅读,若有错误和不足,欢迎指正。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/505159.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

180基于matlab的频率切片小波变换程序(FTWT)

基于matlab的频率切片小波变换程序&#xff08;FTWT&#xff09;。从一种新的角度出发&#xff0c;通过自由选择频率切片函数、引进新尺度参数&#xff0c;在频率域实现小波变换&#xff0c;该变换能够很好地刻画信号各成分之间的相对能量关系。此外&#xff0c;频率切片小波变…

YOLOv9有效提点|加入SGE、Ge、Global Context、GAM等几十种注意力机制(四)

专栏介绍&#xff1a;YOLOv9改进系列 | 包含深度学习最新创新&#xff0c;主力高效涨点&#xff01;&#xff01;&#xff01; 一、本文介绍 本文只有代码及注意力模块简介&#xff0c;YOLOv9中的添加教程&#xff1a;可以看这篇文章。 YOLOv9有效提点|加入SE、CBAM、ECA、SimA…

超强预测算法:XGBoost预测模型

目录 往期精彩内容&#xff1a; 多变量特征序列、单序列数据预测实战 前言 1 风速数据预处理与数据集制作 1.1 导入数据 1.2 多变量数据预处理与数据集制作 1.3 单序列数据预处理与数据集制作 2超强模型XGBoost——原理介绍 3 模型评估和对比 3.1 随机森林预测模型 3…

蓝桥杯Java B组历年真题(2013年-2021年)

一、2013年真题 1、世纪末的星期 使用日期类判断就行&#xff0c;这里使用LocalDate&#xff0c;也可以使用Calendar类 答案 2099 使用LocalDate import java.time.LocalDate; import java.time.format.DateTimeFormatter; // 1:无需package // 2: 类名必须Main, 不可修改p…

重生奇迹mu魔法师用什么技能刷怪

1、在重生奇迹mu中&#xff0c;魔法师刷怪可采用多种技能。其中&#xff0c;火球术、冰箭术、雷电术、火墙术、冰咆哮术等单体伤害技能较为常用&#xff0c;可以快速消灭单个怪物。 2、同时&#xff0c;群体伤害技能如流星雨、冰风暴、雷霆一击等也可在多个怪物间快速切换&…

MVCC及其原理

1. MVCC概述及其原理 多版本并发控制&#xff08;MVCC&#xff0c;Multi-Version Concurrency Control&#xff09;是一种数据库管理技术&#xff0c;用于提高数据库系统在多用户环境中的并发性能&#xff0c;同时保证事务的隔离性&#xff0c;避免了不必要的锁定。MVCC允许在…

详解JavaScript的函数

详解 JavaScript 的函数 函数的语法格式 创建函数/函数声明/函数定义 function 函数名(形参列表) { 函数体 return 返回值; // return 语句可省略 } 函数调用 函数名(实参列表) // 不考虑返回值 返回值 函数名(实参列表) // 考虑返回值 示例代码 //定义的没有参数列表&am…

鸿蒙Harmony应用开发—ArkTS声明式开发(触摸事件)

当手指在组件上按下、滑动、抬起时触发。 说明&#xff1a; 从API Version 7开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 onTouch onTouch(event: (event: TouchEvent) > void) 手指触摸动作触发该回调。 卡片能力&#xff1a; 从…

备战蓝桥杯Day22 - 计数排序

计数排序问题描述 对列表进行排序&#xff0c;已知列表中的数范围都在0-100之间。设计时间复杂度为O(n)的算法。 比如列表中有一串数字&#xff0c;2 5 3 1 6 3 2 1 &#xff0c;需要将他们按照从小到大的次序排列&#xff0c;得到1 1 2 2 3 3 5 6 的结果。那么此时计数排序是…

2000-2021年300+地级市进出口总额数据

2000-2021年300地级市进出口总额数据 1、时间&#xff1a;2000-2021年 2、指标&#xff1a;进出口总额 3、单位&#xff1a;万美元 4、来源&#xff1a;城市年鉴、各省年鉴、城市公报、2021年为城市统计年鉴中进口额出口额加总之后换算成万美元&#xff0c;已尽最大可能进行…

机器学习:集成学习(Python)

一、Adaboost算法 1.1 Adaboost分类算法 adaboost_discrete_c.py import numpy as np import copy from ch4.decision_tree_C import DecisionTreeClassifierclass AdaBoostClassifier:"""adaboost分类算法&#xff1a;既可以做二分类、也可以做多分类&#…

编译链接实战(25)ThreadSanitizer检测线程安全

ThreadSanitizer&#xff08;又称为TSan&#xff09;是一个用于C/C的数据竞争检测器。在并发系统中&#xff0c;数据竞争是最常见且最难调试的错误类型之一。当两个线程并发访问同一个变量&#xff0c;并且至少有一个访问是写操作时&#xff0c;就会发生数据竞争。C11标准正式将…