C++入门基础07:函数定义与声明、函数传参(传值、传地址、传引用)、函数重载

2023-10-26

C++入门基础07:函数定义与声明、函数传参(传值、传地址、传引用)、函数重载

1、函数定义与声明

函数是一起执行一个任务的一组语句。每个程序(C/C++)都有一个主函数 main() ,
所有简单的程序都可以定义其他额外的函数。可以把代码划分到不同的函数中,函数
划分通常是根据每个函数执行一个特定的任务来进行的。

函数定义的语法
函数由一个函数头和一个函数主体组成。下面列出一个函数的所有组成部分:

int max(int x, int y)
{
 //函数体
}

返回类型:一个函数可以返回一个值。return_type 是函数返回的值的数据类型。有些函数执行所需的操作而不返回值,在这种情况下,return_type 是关键字 void。

函数名称:这是函数的实际名称。函数名和参数列表一起构成了函数签名。
参数:参数就像是占位符。当函数被调用时,您向参数传递一个值,这个值被称为实际参数。参数列表包括函数参数的类型、顺序、数量。参数是可选的,也就是说,函数可能不包含参数。
函数主体:函数主体包含一组定义函数执行任务的语句。

函数声明
函数声明会告诉编译器函数名称及如何调用函数。函数的实际主体可以单独定义。
如下是一个函数声明:
int max(int num1, int num2);
在函数声明中,参数的名称并不重要,只有参数的类型是必需的,因此下面也是有效的声明:
int max(int, int);
当您在一个源文件中定义函数且在另一个文件中调用函数时,函数声明是必需的。在这种情况下,您应该在调用函数的文件顶部声明函数。

函数声明告诉编译器函数的名称、返回类型和参数。
函数定义提供了函数的实际主体。
C++中常将声明与定义分开。
函数必须先声明或者定义才能使用。

函数调用
当程序调用函数时,程序控制权会转移给被调用的函数。
被调用的函数执行已定义的任务,当函数的返回语句被执行时,或到达函数的结束括号时,会把程序控制权交还给主程序。
调用函数时,传递所需参数,如果函数返回一个值,则可以存储返回值。

下面这个代码会报错:

#include <iostream>
//系统定义头文件一般是尖括号
#include<fstream>
#include<string>
#include<exception> //标准库的头文件,标准的异常
#include<cstring>
using namespace std;


int print_string(string x)
{
    fun(x);
    if (x.empty())
        return -1;
    cout << x << endl;
    return 0;
}

string fun(string str)
{
    return str;
}

int main()
{
    string str = "cplusplus";
    int res = print_string(str);
    if(res == -1)
    {
        cout << "input string is empty!" << endl;
    }

    return 0;
}

报错内容是找不到fun标识符。
在这里插入图片描述
这是由于fun函数是在print_string函数之后,那么在定义print_string函数的时候,fun函数还没定义,就不知道这个函数的名称。所以会报错。函数还没有声明。

如果要解决这个报错就需要将这个fun函数的声明放到print_string函数之前。
下面代码是将fun函数在print_string函数前声明了,就不会报错了。

#include <iostream>
//系统定义头文件一般是尖括号
#include<fstream>
#include<string>
#include<exception> //标准库的头文件,标准的异常
#include<cstring>
using namespace std;

string fun(string str); //声明fun函数,fun函数在print_string函数前声明了,就不会报错了。
//参数的名称并不重要,这里我们将参数名删除也是不会报错的。

int print_string(string x)
{
    fun(x);
    if (x.empty())
        return -1;
    cout << x << endl;
    return 0;
}

string fun(string str)
{
    return str;
}

int main()
{
    string str = "cplusplus";
    int res = print_string(str);
    if(res == -1)
    {
        cout << "input string is empty!" << endl;
    }

    return 0;
}

在这里插入图片描述

此外在声明函数时参数的名称并不重要,这里我们将参数名删除也是不会报错的。
就如下面代码中fun函数的声明,参数的参数名删除了,也没有报错,是可以编译成功的。

#include <iostream>
//系统定义头文件一般是尖括号
#include<fstream>
#include<string>
#include<exception> //标准库的头文件,标准的异常
#include<cstring>
using namespace std;

//string fun(string str); //声明fun函数,fun函数在print_string函数前声明了,就不会报错了。
string fun(string ); //fun函数的声明,参数的参数名删除了,不会报错。


int print_string(string x)
{
    fun(x);
    if (x.empty())
        return -1;
    cout << x << endl;
    return 0;
}

string fun(string str)
{
    return str;
}

int main()
{
    string str = "cplusplus";
    int res = print_string(str);
    if(res == -1)
    {
        cout << "input string is empty!" << endl;
    }

    return 0;
}

在这里插入图片描述

2、 函数传参

函数传参有三种传参方式:传值、传址、传引用。
1、按值传递
(1)形参和实参各占一个独立的存储空间。
(2)形参的存储空间是函数被调用时才分配的,调用开始,系统为形参开辟一个临时的存储区,然后将各实参传递给形参,这是形参就得到了实参的值。
2、地址传递
地址传递与值传递的不同在于,它把实参的存储地址传送给形参,使得形参指针和实参指针指向同一块地址。因此,被调用函数中对形参指针所指向的地址中内容的任何改变都会影响到实参。
3、引用传递
引用传递是以引用为参数,则既可以使得对形参的任何操作都能改变相应数据,又使函数调用方便。引用传递是在形参调用前加入引用运算符“&”。引用为实参的别名,和实参是同一个变量,则他们的值也相同,该引用改变则它的实参也改变。

传值与传引用的差别:
1 传引用时,形参和实参是同一个变量,即使用相同的内存空间,二者有相同的地址。而传值时二者地址不同;
2 传引用时,由于没有新建变量,所以对于类对象参数,不会产生构造和析构。而如果是传值调用,调用时会进行构造,退出函数时会进行析构,会有性能的消耗,能传引用的地方就传引用;
3 由于传引用使用的是原本实参的地址,所以对引用参数值的修改,会在退出函数后体现在主调函数中,而传值调用对参数的修改不会影响到主调函数。

传地址的例子(传指针):
可以发现输出cout的地址是一样的。

#include <iostream>
//系统定义头文件一般是尖括号
#include<fstream>
#include<string>
#include<exception> //标准库的头文件,标准的异常
#include<cstring>
using namespace std;

//传指针
int print_string(string *x)
{
    if ((*x).empty()) //这里(*x)是对指针的一个解引用。
        return -1;
    cout << "取x地址:" << x << endl;
    return 0;
}

int main()
{
    string str = "cplusplus";
    int res = print_string(&str); //传一个地址,取str变量的地址。
    cout << "取str地址:" << &str << endl;
    if(res == -1)
    {
        cout << "input string is empty!" << endl;
    }

    return 0;
}

在这里插入图片描述

传引用的例子:
引用相当于实际参数的一个别名,所以可以直接使用。也就是和传实际参数是一样的。
引用的参数改变,对应的原本参数值也会改变。就如下面这个代码:

#include <iostream>
//系统定义头文件一般是尖括号
#include<fstream>
#include<string>
#include<exception> //标准库的头文件,标准的异常
#include<cstring>
using namespace std;


//传引用
//引用相当于实际参数的一个别名,所以可以直接使用。也就是和传实际参数是一样的。
int print_string(string &x)
{
    if (x.empty()) //这里(*x)是对指针的一个解引用。
        return -1;
    cout << "x的内容:" << x << endl;
    x = "cpp";//这里改变了参数的值。
    cout << "x改变内容:" << x << endl;
    return 0;
}

int main()
{
    string str = "cplusplus";
    int res = print_string(str); //将变量本身传进去。
    cout << "str的内容:" << str << endl; //这里可以看到变量本身也是改变了。
    if(res == -1)
    {
        cout << "input string is empty!" << endl;
    }

    return 0;
}

在这里插入图片描述

3、函数重载

C++ 允许在同一作用域中的某个函数指定多个定义,这就是函数重载。

重载声明是指一个与之前已经在该作用域内声明过的函数或方法具有相同名称的声明,但是它们的参数列表和定义(实现)不相同。

当调用一个重载函数时,编译器通过把你所使用的参数类型与定义中的参数类型进行比较,决定选用最合适的定义。选择最合适的重载函数的过程,称为重载决策。

在同一个作用域内,可以声明几个功能类似的同名函数,但是这些同名函数的形参(如参数的个数、类型或者顺序)必须不同,不能仅通过返回类型的不同来重载函数。

如下代码中同名函数 print() 被用于输出不同的数据类型:

 void print(int i) {
 cout << "整数为: " << i << endl;
 }
 void print(double f) {
 cout << "浮点数为: " << f << endl;
 }
 void print(char c[]) {
 cout << "字符串为: " << c << endl;
 }

注意,参数列表不同包括参数的个数不同、类型不同或顺序不同,仅仅参数名称不同是不可以的。函数返回值也不能作为重载的依据。

函数的重载的规则:
函数名称必须相同。

参数列表必须不同(个数不同、类型不同、参数排列顺序不同等)。

函数的返回类型可以相同也可以不相同。

仅仅返回类型不同不足以成为函数的重载。

C++代码在编译时会根据参数列表对函数进行重命名(不同的编译器有不同的重命名方式,这里仅仅举例说明,实际情况可能并非如此),例如 void print(int i)会被重命名为_print_int,void print(float f)会被重命名为_print_float。当发生函数调用时,编译器会根据传入的实参去逐个匹配,以选择对应的函数,如果匹配失败,编译器就会报错。

因此,函数重载只是在用户层面看到的函数名称是一致的,本质上它们还是不同的函数,占用不同的内存,函数地址也不一样。

#include <iostream>
//系统定义头文件一般是尖括号
#include<fstream>
#include<string>
#include<exception> //标准库的头文件,标准的异常
#include<cstring>
using namespace std;

//定义了三个函数,这三个函数的参数列表不同。
//这三个函数的功能都是返回较大的那个值。

int mymax(int x, int y)
{
    cout << "call max int!" << endl;
    return (x > y) ? x : y;
}

float mymax(float x, float y)
{
    cout << "call max float!" << endl;
    return (x > y) ? x : y;
}

char mymax(char x, char y)
{
    cout << "call max char!" << endl;
    return (x > y) ? x : y;
}

int main()
{
    int a = 10, b = 100;
    cout << mymax(a, b) << endl;

    char c = 'a', d = 'A';
    cout << mymax(c, d) << endl;

    float e = 10.1f, f = 100.3f;
    cout << mymax(e, f) << endl;

    return 0;
}

在这里插入图片描述

这里将float型和char型的mymax注释掉了。可以看到依然是可以执行的,都是调用的int型的mymax函数了,只不过都进行隐式转换,转换到了int类型。

#include <iostream>
//系统定义头文件一般是尖括号
#include<fstream>
#include<string>
#include<exception> //标准库的头文件,标准的异常
#include<cstring>
using namespace std;

//定义了三个函数,这三个函数的参数列表不同。
//这三个函数的功能都是返回较大的那个值。

int mymax(int x, int y)
{
    cout << "call max int!" << endl;
    return (x > y) ? x : y;
}

//float mymax(float x, float y)
//{
//    cout << "call max float!" << endl;
//    return (x > y) ? x : y;
//}
//
//char mymax(char x, char y)
//{
//    cout << "call max char!" << endl;
//    return (x > y) ? x : y;
//}

int main()
{
    int a = 10, b = 100;
    cout << mymax(a, b) << endl;

    char c = 'a', d = 'A';
    cout << mymax(c, d) << endl;

    float e = 10.1f, f = 100.3f;
    cout << mymax(e, f) << endl;

    return 0;
}

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++入门基础07:函数定义与声明、函数传参(传值、传地址、传引用)、函数重载 的相关文章

  • 4 x 3 锁图案

    我遇到了这个 它要求计算在 4x3 网格中可以制作特定长度的锁定图案的方式数 并遵循规则 可能有些点不能包含在路径中 有效的模式具有以下属性 图案可以使用第一次接触的点序列来表示 与绘制图案的顺序相同 从 1 1 到 2 2 的图案与图案不
  • 我想优化这个短循环

    我想优化这个简单的循环 unsigned int i while j 0 j is an unsigned int with a start value of about N 36 000 000 float sub 0 i 1 unsig
  • WritePrivateProfileString 未在末尾添加属性

    我正在使用以下命令在 ini 文件中写入一些属性WritePrivateProfileString函数并且一切正常 但是当我添加多行文本时 出现了问题 这是代码和输出 WritePrivateProfileString T General
  • 更改 WinForms 按钮突出显示颜色

    I found 这一页 https stackoverflow com questions 9260303 how to change menu hover color winforms 其中概述了如何更改 MenuStrip 及其项目的呈
  • 改变 RGB 颜色的色调

    我正在尝试编写一个函数来改变 RGB 颜色的色调 具体来说 我在 iOS 应用程序中使用它 但数学是通用的 下图显示了 R G 和 B 值如何随色调变化 看起来 编写一个函数来改变色调似乎应该是一个相对简单的事情 而不需要对不同的颜色格式进
  • 此上下文中仅支持实体类型、枚举类型或基本类型

    我目前正在开发一个搜索页面 我只需要返回主题的主题详细信息列表 其中包含存储在 int ST 中的所有主题标签 id 目前 ST null true ST Contains b ThemeTagID 行似乎给了我一个错误 附加信息 无法创建
  • 在大型数据绑定 ObservableCollection 中添加/删除许多项目,而无需冻结 GUI

    我和我的团队正在开发一个 WPF 应用程序 该应用程序显示多个并发 XamDataChart 控件 由 Infragistics 提供 每个图表都绑定到不同的 ObservableCollection 最多可包含 200 万个点 对于每个图
  • 在 C 中使用另一个结构中的二维结构数组的编码问题

    我正在使用一个二维结构数组 它是另一个结构的一部分 这不是我做过很多事的事情 所以我遇到了问题 该函数在接近末尾的 测试 for 循环后最终失败 它在出现段错误之前正确打印出一行 我的代码中将数据读入虚拟二维结构数组的部分工作得很好 所以它
  • 使用来自不同线程的实时数据更新 QTableView 的最佳策略

    我的应用程序现在启动几个线程 如 5 10 个 来从不同源收集数据 它们与主 GUI 线程分离 因此我在 GUI 中感觉不到任何缓慢 并且我可以在后台线程工作时继续工作 一切都很棒 但现在我希望能够在我的主 GUI 中的 QTableVie
  • 最佳实践:从属性中抛出异常

    什么时候适合从属性 getter 或 setter 中抛出异常 什么时候不合适呢 为什么 关于这个主题的外部文档的链接会很有帮助 谷歌搜索结果出奇的少 Microsoft 在以下位置提供了有关如何设计属性的建议 http msdn micr
  • 在 C 或 C++ 中使用逗号作为宏名称

    我想做这样的事情 define define MAX 10 000 000 undef 有什么技巧可以做到吗 编辑 我知道 C 14 中的数字分隔符 我正在寻找一种技巧来对不兼容的编译器执行相同的操作 EDIT2 请考虑Variadic M
  • 让 AutoMapper 自动映射前缀属性

    我希望 AutoMapper 自动映射成员 如下所示 class Model public int ModelId get set class ModelDto public int Id get set 在这里 我会做一个 CreateM
  • 类型“MyApp”已包含“MystatusBar”的定义

    我的应用程序 XAML
  • 模板是如何实例化的?

    这是一个练习 来自C 入门第五版 练习 16 27 对于每个带标签的语句 解释什么 如果有 实例化发生 如果实例化了模板 请解释原因 如果 不 请解释为什么不 第677页 template
  • 如何在 C# 中读取 Visio 文档内容

    我的DLL库代码如下 using System using IVisio Microsoft Office Interop Visio namespace Emix public class Visio protected String p
  • 在 asp.net MVC 控制器中调用异步外部 Web 服务

    在 Asp net MVC 控制器 GET 方法 中 我调用外部 Web 服务 用于 IP 地理定位 返回 IP 位置的 json 数据 如何使调用异步 以便堆栈可以在等待服务响应时继续 当 GEO IP 请求完成后 我希望能够更新数据库
  • 使用 JSON.NET 反序列化一些 JSON

    我对 JSON 非常陌生 我需要解析 API 提供的一些内容 谷歌快速搜索出现了JSON NET http james newtonking com pages json net aspx 所以我现在尝试使用它将此 JSON 解析为列表对象
  • 计算 .NET Core 项目的代码指标?

    我正在研究 ASP NET Core 和 NET Core 项目 对于经典的 C 项目 Visual Studio 2015 具有计算代码指标的功能 对于 NET Core 预览版 2 工具中缺少支持 在工具更加完整之前 有人知道解决方法吗
  • 如何包装实体框架以在执行前拦截 LINQ 表达式?

    我想在执行之前重写 LINQ 表达式的某些部分 我在将重写器注入正确的位置时遇到问题 实际上根本没有 查看实体框架源代码 在反射器中 它最终归结为IQueryProvider Execute在 EF 中 它通过以下方式耦合到表达式Objec
  • 提高批量请求的野兽内存使用率

    我运行这个boost beast 客户端 异步 ssl http www boost org doc libs develop libs beast example http client async ssl http client asy

随机推荐

  • 局部最小值问题

    问题 一个数组 相邻不等 返回任意一个局部最小值 重点是 相邻不等 否则无法用此方法 分析 所谓局部最小值 即左右相邻的数都比他大 当此数为第一个时 只需要右边的比他大即可 最右同理 代码 生成随机数组 相邻不等 void Random a
  • B站疯传!堪称最强!java超级面试资料

    我没有知名企业的工作经历 也没有多么耀目的成就 为什么他们会对我有那么深的印象呢 其实 在我看来 面试都是有迹可循的 也就是说 完全可以用很短的时间准备 却给面试官留下很深的印象 一 好的自我介绍决定了面试的80 不管你相不相信 你适不适合
  • DataX理论知识:简介-框架设计-数据抽取策略

    文章目录 一 简介 二 框架设计 三 数据抽取策略 一 简介 DataX 是一个 异构数据源 离线同步工具 可实现 各种 异构数据源 之间 稳定高效的数据同步功能 设计理念 从 蜘蛛网 到 星型链路 DataX充当一个中转站的角色 二 框架
  • 数据分析——数据特征描述、画箱线图、分组直方图

    数据特征描述 import pandas as pd catering sale r H school 数据挖掘 实验 实验二 catering sale xls data pd read excel catering sale index
  • 如何对SQL Server中的tempdb“减肥”

    SQL Server会自动创建一个名为tempdb的数据库作为工作空间使
  • checkstyle:off 使用注释暂时禁用checkstyle检查

    背景 本文介绍在Gradle中 如何跳过checkstyle对指定的文件 或者指定的代码块 的检查 步骤 1 在checkstyle xml的
  • Typescript常见表达式

    Typescript常见表达式 一 析构表达式 destructuring 1 数组析构表达式 用中括号括起来 var array1 1 2 3 4 function doSomething number1 number2 others c
  • Java基础---反射、多线程

    十 反射机制 1 Java反射机制概述 1 1Java Reflection Reflection 反射 是被犯为动态语言的关键 反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息 并能直接操作任意对象的内部属性
  • datetime.time类介绍

    一 time是一个时间类 由时 分 秒 微妙组成 其构造函数如下 class datetime time hour minute second microsecond tzinfo 参数tzinfo 它表示时区信息 各参数的取值范围 hou
  • window安装docker Desktop和wsl2

    目录 一 先到微软商店下载terminal 也就是power shell 后续命令都在这个里面执行 二 安装docker Destop 1 打开控制面板 2 勾选Hyper V服务 3 根据提示重启电脑 等待更新即可 二 启动Docker
  • 字符串去重的5种方式

    public class Demo public static void main String args String str albcad12l gt sfg gt String newStr quChong5 str System o
  • 深度负反馈

    负反馈放大电路的方块图 因为负反馈放大电路有四种组态 而且对于同一种组态 具体电路也各不相同 所以为了研究负反馈放大电路的共同规律 可以利用方块图来描述所有电路 一 负反馈放大电路的方块图表示法 任何负反馈放大电路都可以用下图所示的方块图来
  • windows服务器禁用135,137,138,139,445端口方法

    windows服务器禁用135 137 138 139 445端口方法 1 防火墙新建入站和出站规则 注意 此方法只针对防火墙已开启的情况下才能实现禁用端口 打开控制面板 系统和安全 Windows Defender 防火墙 在左侧选择 高
  • 安装Apache Hive-2.3.3

    1 Hive是什么 1 1 Hive是数据仓库 数据仓库英文名DataWarehouse 可简写为DW或DWH 数据仓库 由数据仓库之父比尔 恩门 Bill Inmon 于1990年提出 主要功能仍是将组织透过资讯系统之联机事务处理 OLT
  • 【H.264/AVC视频编解码技术详解】十七:帧内预测编码的预测实现方法

    H 264 AVC视频编解码技术详解 视频教程已经在 CSDN学院 上线 视频中详述了H 264的背景 标准协议和实现 并通过一个实战工程的形式对H 264的标准进行解析和实现 欢迎观看 纸上得来终觉浅 绝知此事要躬行 只有自己按照标准文档
  • pandas.read_csv参数整理

    pandas read csv参数整理 转载 读取CSV 逗号分割 文件到DataFrame 也支持文件的部分导入和选择迭代 更多帮助参见 http pandas pydata org pandas docs stable io html
  • 如何当个优秀的文档工程师?从 TC China 看技术文档工程师的自我修养

    本文系 NebulaGraph Community Academic 技术文档工程师 Abby 的参会观感 讲述了她在中国技术传播大会分享的收获以及感悟 据说 技术内容领域 传播领域的专家和决策者们会在中国技术传播大会 tcworld Ch
  • 实施静态分析并非易事

    针对软件错误和漏洞的静态分析测试 SAST 应该成为应用程序安全性和软件质量程序的一部分 您需要做的就是运行一个工具 它将在开发的早期发现价格便宜且易于修复的错误 听起来很简单 但是 这不仅需要购买工具并运行扫描 还需要将代码上传到测试服务
  • 【云原生】Docker镜像、容器、仓库、配置等常见问题汇总(面试必看)

    Docker 常见问题整理汇总 本文目录 一 镜像相关 二 容器相关 三 仓库相关 四 配置相关 五 Docker与虚拟化 六 其它常见问题 一 镜像相关 1 如何批量清理临时镜像文件 可以使用sudo docker rmi sudo do
  • C++入门基础07:函数定义与声明、函数传参(传值、传地址、传引用)、函数重载

    C 入门基础07 函数定义与声明 函数传参 传值 传地址 传引用 函数重载 1 函数定义与声明 函数是一起执行一个任务的一组语句 每个程序 C C 都有一个主函数 main 所有简单的程序都可以定义其他额外的函数 可以把代码划分到不同的函数