C++(6):函数

news2024/11/27 22:28:49

函数基础

典型的函数包括:返回类型、函数名字、由 0 个或多个形参组成的列表以及函数体。
通过调用运算符(call operator)来执行函数。

调用运算符的形式是一对圆括号,它作用于一个表达式,该表达式是函数或者指向函数的指针;圆括号之内是一个用逗号隔开的实参(argument)列表,我们用实参初始化函数的形参。调用表达式的类型就是函数的返回类型。

函数的调用会完成两项工作:
1.用实参初始化函数对应的形参;
2.将控制器转移给被调用函数,主调函数的执行被暂时中断,被调函数开始执行。

执行函数的第一步是(隐式地)定义并初始化它的形参。

return 语句完成两项工作:
1.返回 return 语句中的值(如果有的话)
2.将控制权从被调函数转移回主调函数。

实参是形参的初始值。
函数的形参列表可以为空,但是不能省略。

返回类型不能是数组或函数类型,但是可以是数组指针或函数指针。

局部对象

名字有作用域,对象有生命周期。
1.名字的作用域是程序文本的一部分,名字在其中可见。
2.对象的生命周期是程序执行过程中该对象存在的一段时间。

函数体是一个语句块。块构成一个新的作用域,我们可以在其中定义变量。形参和函数体内部定义的变量统称为局部变量(local variable)。它们对函数而言是“局部”的,仅在函数的作用域内可见,同时局部变量还会隐藏在外层作用域中同名的其他所有声明中。

局部变量的生命周期依赖于定义的方式:
1.自动对象(只存在于块执行期间的对象):当块的执行结束后,块中创建的自动对象的值就变成未定义的了。
形参是一种自动对象。函数开始时为形参申请存储空间,因为形参定义在函数体作用域之内,所以一旦函数终止,形参也就被销毁。
内置类型的未初始化局部变量将产生未定义的值。
2.局部静态对象(static 型):局部静态对象在程序的执行路径第一次经过对象定义语句时初始化,并且直到程序终止才被销毁,在此期间即使对象所在的函数结束执行也不会对它有影响。

函数声明

函数的名字也必须在使用之前声明。
函数的声明和函数的定义非常类似,唯一的区别是函数声明无须函数体,用一个分号替代即可。

函数的三要素(返回类型、函数名、形参类型)描述了函数的接口,说明了调用该函数所需的全部信息。函数声明也称作函数原型(function prototype)。

含有函数声明的头文件应该被包含到定义函数的源文件中。

参数传递

每次调用函数时都会重新创建它的形参,并用传入的实参对形参进行初始化。

形参的类型决定了形参和实参交互的方式:如果形参是引用类型,它将绑定到对应的实参上;否则,将实参的值拷贝后赋给形参。

指针形参:当执行指针拷贝操作时,拷贝的是指针的值。拷贝之后,两个指针是不同的指针。

引用形参在传递实参时直接传入对象。
如果函数无须改变引用形参的值,最好将其声明为常量引用。

const 形参和实参

当用实参初始化形参时会忽略掉顶层const,即形参类型为 const int 和 const 是一样的。

const int ci = 42;//不能改变ci,const是顶层的
int i = ci;//正确:当拷贝ci时,忽略了它的顶层const 
int * const p = &i;//const是顶层的,不能给p赋值
*p= 0;//正确:通过p改变对象的内容是允许的,现在i变成了0

尽量使用常量引用

数组形参

三种等价声明:

void print(const int*);
void print(const int[]);
void print(const int[10]);

管理指针形参有三种常用的技术:
1.使用标记指定数组长度;(例如 cout 会输出到字符串的空字符停止)
2.使用标准库规范;(例如使用while循环判断)
3.显式传递一个表示数组大小的形参。

形参也可以是数组的引用。此时,引用形参绑定到对应的实参上,也就是绑定到数组上:

//正确:形参是数组的引用,维度是类型的一部分
void print(int (&arr)[10]){
	for (auto elem: arr)
		cout << elem <<endl;
}

//&arr两端的括号必不可少:
f(int &arr[10])//错误:将arr声明成了引用的数组
f(int (&arr)[10])//正确:arr是具有10个整数的整型数组的引用

可以给 main 函数传递实参

int main (int argc,char *argv[]){
}
int main (int argc, char **argv){
}

含有可变形参的函数

有时无法提前预知应该向函数传递几个实参。
编写能处理不同数量实参的函数:
1.如果所有的实参类型相同,可以传递一个名为 initializer_list 的标准库类型;
2.如果实参的类型不同,可以编写一种特殊的函数,也就是所谓的可变参数模板

initializer_list 提供的操作:
在这里插入图片描述
省略符形参
省略符形参只能出现在形参列表的最后一个位置.

void foo(parm_list, ...);
void foo (...);

返回类型和 return 语句

return;//终止当前正在执行的函数,将控制权返回到调用该函数的地方
return expression;//返回 return 语句中的值	

无返回值函数

没有返回值的 return 语句只能用在返回类型是 void 的函数中。返回 void 的函数最后一句后面会隐式地执行 return。

有返回值函数

只要函数地返回类型不是 void ,则该函数内地每条 return 语句必须返回一个值。return 语句返回值地类型必须与函数地返回类型相同,或者能隐式地转换成函数地返回类型。

返回的值用于初始化调用点的一个临时量,该临时量就是函数调用的结果。

不要返回局部对象的引用或指针:函数终止意味着局部变量的引用将指向不再有效的内存区域。

函数的返回类型决定函数调用是否是左值:调用一个返回引用的函数得到左值,其他返回类型得到右值。

char &get_val(string &str,string:: size_type ix){
	return str[ix] ;//get_val假定索引值是有效的
}

int main(){
	string s("a value");
	get_val(s,0)='A';//将 s[0]的值改为A
	cout<<s<<endl;//输出 A value
	return 0;
}

允许 main 函数没有 return 语句直接结束,编译器隐式地插入一条 return 0;

递归: 函数调用它本身,不管是直接还是间接。

//计算val 的阶乘,即1*2* 3 ...* val
int factorial (int val){
	if (val >1)
		return factorial (val-1) *val;
	return 1;
}

main函数不能调用它自己。

返回数组指针

int arr[10];// arr是一个含有10个整数的数组
int *p1[10];// p1是一个含有10个指针的数组
int (*p2)[10] = &arr;// p2是一个指针,它指向含有10个整数的数组

返回数组指针的函数形式:Type (* function (parameter_list) )[dimension]

func(int i);//表示调用func函数时需要一个int类型的实参。
(*func(int i));//意味着可以对函数调用的结果执行解引用操作。
(*func(int i))[10];//表示解引用func的调用将得到一个大小是10的数组。
int(*func(int i))[10];//表示数组中的元素是int类型。

任何函数的定义都能使用尾置返回。尾置返回类型跟在形参列表后面并以一个->符号开头。

//func接受一个int类型的实参,返回一个指针,该指针指向含有10个整数的数组
auto func(int i)-> int(*)[10];

**decltype:**如果知道函数返回的指针将指向哪个数组,就可以使用 decltype 关键字声明返回类型。

int odd[] ={1,3,5,7.9};
int even [] = {0,2,4,6,8};
//返回一个指针,该指针指向含有5个整数的数组
decltype(odd) *arrPtr(int i){
	return(i % 2)? &odd : &even;//返回一个指向数组的指针
}

要返回数组指针必须在函数声明时要加一个 *

函数重载

main函数不能重载。
调用重载函数时应尽量避免强制类型转换。

函数匹配

函数匹配:指在一个过程,把函数调用与一组重载函数中的某一个关联起来。函数匹配也叫做重载确定。
编译器首先将调用的实参与重载集合中每一个函数的形参进行比较,然后根据比较的结果决定到底调用哪个函数。

函数匹配的第一步是选定本次调用对应的重载函数集,集合中的函数称为候选函数。
候选函数的两个特征:一是与被调用的函数同名,二是其声明在调用点可见。
第二步考察本次调用提供的实参,然后从候选函数中选出能被这组实参调用的函数,这些新选出的函数称为可行函数。
可行函数的两个特征:一是其形参数量与本次调用提供的实参数量相等,二是每个实参的类型与对应的形参类型相同,或者能转换成形参的类型。

调用重载函数时地三种可能结果:
1.编译器找到一个与实参最佳匹配(best match)的函数,并生成调用该函数的代码。
2.找不到任何一个函数与调用的实参匹配,此时编译器发出无匹配(no match)的错误信息。
3.有多于一个函数可以匹配,但是每一个都不是明显的最佳选择。此时也将发生错误,称为二义性调用(ambiguous call)。

特殊用途语言特性

默认实参

调用含有默认实参的函数时,可以包含该实参,也可以省略该实参。
函数的后续声明只能为之前那些没有默认值的形参添加默认实参,而且该形参右侧的所有形参必须都有默认值。

通常,应该在函数声明中指定默认实参,并将该声明放在合适的头文件中。

局部变量不能作为默认实参。

内联函数和 constexpr 函数

内联函数可避免函数调用的开销,通常就是将它在每个调用点上“内联地”展开。inline 内联说明只是向编译器发出的一个请求,编译器可以选择忽略这个请求。

内联机制用于优化规模较小、流程直接、频繁调用的函数。

constexpr 函数指能用于常量表达式的函数。constexpr函数被隐式地指定为内联函数。
定义 constexpr 函数需要:1.函数的返回类型及所有形参的类型都是字面值类型 ;2.函数体中必须有且只有一条 return 语句。

constexpr int new_sz() { return 42; }
constexpr int foo = new_sz();//正确: foo是一个常量表达式

constexpr函数体内也可以包含其他语句,只要这些语句在运行时不执行任何操作就行。例如,constexpr函数中可以有空语句、类型别名以及using声明。

constexpr函数不一定返回常量表达式。

应该把内联函数和constexpr函数的定义放到头文件里。

调试帮助

assert 预处理宏使用一个表达式作为它的条件:assert(expr);
首先对expr求值,如果表达式为假(即 0),assert输出信息并终止程序的执行。如果表达式为真(即非0),assert什么也不做。assert宏常用于检查“不能发生”的条件。

assert宏定义在cassert头文件中。和预处理变量一样,宏名字在程序内必须唯一。

NDEBUG 预处理变量
assert的行为依赖于一个名为NDEBUG的预处理变量的状态:如果定义了 NDEBUG,则 assert 什么也不做,默认状态下没有定义 NDEBUG。

预处理器定义的对于程序调试很有用的名字:

__func__;//局部静态变量,用于当前函数名的字符串字面值
__FILE__;//当前文件名的字符串字面值
__LINE__;//当前行号的整型字面值
__TIME__;//文件编译时间的字符串字面值
__DATE__;//文件编译日期的字符串字面值

函数指针

函数指针指向的是函数而非对象。
函数指针指向某种特定类型。

// pf指向一个函数,该函数的参数是两个const string的引用,返回值是boo1类型
bool (*pf)(const string &,const string &);//未初始化

//*pf两端的括号必不可少。如果不写这对括号,则pf是一个返回值为bool指针的函数:
//声明一个名为pf的函数,该函教返回bool*
bool *pf (const string &, const string &);

把函数名作为一个值使用时,该函数自动地转换成指针:

pf = lengthcompare;//pf指向名为 lengthcompare 的函数
pf =& lengthCompare;//等价的赋值语句:取地址符是可选的

返回指向函数类型的指针
使用类型别名:

using F=int(int*, int);//F是函数类型,不是指针
using PF = int(*) (int*, int);//PF 是指针类型

显式指定:

PF f1 (int);//正确:PF是指向函数的指针,f1返回指向函数的指针
F f1(int);//错误:F是函数类型,f1不能返回一个函数
F *f1 (int);//正确:显式地指定返回类型是指向函数的指针

重要术语

  1. 二义性调用(ambiguous call) 是一种编译时发生的错误,造成二义性调用的原因是在函数匹配时两个或多个函数提供的匹配一样好,编译器找不到唯一的最佳匹配。
  2. 自动对象(automatic object) 仅存在于函数执行过程中的对象。当程序的控制流经过此类对象的定义语句时,创建该对象:当到达了定义所在的块的末尾时,销毁该对象。
  3. 最佳匹配(best match) 从一组重载函数中为调用选出的一个函数。如果存在最佳匹配,则选出的函数与其他所有可行函数相比,至少在一个实参上是更优的匹配,同时在其他实参的匹配上不会更差。
  4. 函数匹配(function matching) 编译器解析重载函数调用的过程,在此过程中,实参与每个重载函数的形参列表逐一比较。
  5. 对象生命周期(object lifetime) 每个对象都有相应的生命周期。块内定义的非静态对象的生命周期从它的定义开始,到定义所在的块末尾为止。程序启动后创建全局对象,程序控制流经过局部静态对象的定义时创建该局部静态对象;当main函数结束时销毁全局对象和局部静态对象。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

1731_makefile编写小结1_编译同目录下的文件

全部学习汇总&#xff1a; GreyZhang/g_makefile: Learn makefile from all kinds of tutorials on the web. Happy hacking and lets find an common way so we may dont need to touch makefile code any more! (github.com) 欢迎路过的YUAN类朋友相互交流&#xff0c;以下是…

每日学术速递6.2

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.CL 1.BiomedGPT: A Unified and Generalist Biomedical Generative Pre-trained Transformer for Vision, Language, and Multimodal Tasks 标题&#xff1a;BiomedGPT&#xff1a;用于…

chatgpt赋能python:Python反向99乘法表:简单易学的终极练习

Python反向99乘法表&#xff1a;简单易学的终极练习 Python是一门强大而又容易上手的编程语言&#xff0c;而反向99乘法表则是一个极佳的练手项目。不仅能锻炼Python的基本语法和逻辑思维&#xff0c;同时也能体现出代码的风格和美感。本文将以Python反向99乘法表为例&#xf…

基于matlab仿真L形金属块基于时间温度分布图

一、前言 此示例说明了如何使用 Simulink 3D 动画™和 MATLAB 接口来操作复杂对象。 在此示例中&#xff0c;矩阵类型的数据在 MATLAB 和虚拟现实世界之间传输。使用此功能&#xff0c;您可以实现大量的颜色变化或变形。这对于可视化各种物理过程很有用。 我们在L形金属块中使用…

Chain of Thought Prompting和Zero Shot Chain of Thought初步认识

1. 思维链提示&#xff08;Chain-of-Thought Prompting&#xff09; 思维链(Chain-of-Thought:CoT)提示过程是一种最近开发的提示方法&#xff0c;它鼓励大语言模型解释其推理过程。下图显示了 few shot standard prompt&#xff08;左)与链式思维提示过程&#xff08;右&…

ChatGPT提示词攻略之基本原则

下面是调用openai的completion接口的函数。但在本文中并不是重点。了解一下就好。 import openai import osfrom dotenv import load_dotenv, find_dotenv _ load_dotenv(find_dotenv())openai.api_key os.getenv(OPENAI_API_KEY)def get_completion(prompt, model"gp…

[LeetCode周赛复盘] 第 348场周赛20230604

[LeetCode周赛复盘] 第 348场周赛20230604 一、本周周赛总结6462. 最小化字符串长度1. 题目描述2. 思路分析3. 代码实现 6424. 半有序排列1. 题目描述2. 思路分析3. 代码实现 6472. 查询后矩阵的和1. 题目描述2. 思路分析3. 代码实现 6396. 统计整数数目1. 题目描述2. 思路分析…

10.全局配置 app.json 与页面配置

常用的配置项有 pages 小程序的所有页面window 小程序窗口的外观tabBar 小程序底部的tabBar效果&#xff0c;就是底部的切换那部分style 组件样式版本 目录 1 window 2 tabBar 3 页面配置 1 window 小程序由下面三个部分组成&#xff0c;window可以配置 导航栏区域 与…

JavaSE_day43(多线程单线程区别,图解main方法若是单多线程该如何执行,如何使用多线程2种方式)

1 A.java * 学习多线程之前&#xff0c;我们先要了解几个关于多线程有关的概念。 A:进程&#xff1a;进程指正在运行的程序。确切的来说&#xff0c;当一个程序进入内存运行&#xff0c;即变成一个进程&#xff0c;进程是处于运行过程中的程序&#xff0c;并且具有一定…

【生成数据】绘制简单的折线图

使用scatter绘制散点图并设置其样式 plt.scatter(2, 4, s200)#设置图表标题并给坐标轴加上标签 plt.title("Square Number", fontsize24) plt.xlabel("Value", fontsize14) plt.ylabel("Square of Value", fontsize14)#设置刻度标记的大小 plt.…

2022年,Rust与Go哪一个更好?

这是每一个程序员和开发人员都问过的问题&#xff0c;还有很多人仍然在问&#xff0c;即使他们已经做出了自己的决定。Rust vs. Go。2022年&#xff0c;我应该选择哪一个&#xff1f;或选择哪种语言--Golang或Rust。 Golang和Rust是目前使用的最年轻的编程语言。Go于2009年在谷…

最新ChatGPT4.0Plus开通教程-支付宝购买苹果礼品卡-亲测可用

2023.06.04亲测可用ChatGPT开通Plus教程 前言&#xff1a;一、准备工作二、购买苹果礼品卡一、官网购买礼品卡二、支付宝方式购买 三、AppStore充值礼品卡四、ChatGPT Plus 订阅五、iOS 端 ChatGPT Plus 订阅失败解决方法六、美区AppStore账号ID注册教程&#xff1a; 之前&…

【Svelte】一个简单的前端框架

Svelte.js的学习成本高吗&#xff1f; Svelte是新手编码初学者的完美平台。只需一个HTML/CSS和JavaScript技能组合&#xff0c;您就可以从头开始构建您的第一个网站&#xff0c;而无需额外的知识。 这使得学习曲线非常小&#xff0c;不像它的大多数替代方案。除此之外&#xf…

ChatGLM-6b 多任务微调

ChatGLM-6b也是一种预训练模型&#xff0c;它也可以通过微调来适应下游任务。实验表明&#xff0c;使用ChatGLM-6b微调和Bert类预训练模型微调的效果相近。如果采用多任务设计&#xff0c;ChatGLM-6b的效果会更好。你可以在这里了解更多关于ChatGLM-6B的信息: ChatGLM-6Bhttps:…

边缘化中FEJ图例的理解

如图所示&#xff0c;在解释为什么需要FEJ(First Estimation Jacobian)时&#xff0c;通常会将这个图拿出来说事。但是&#xff0c;很多时候只是一笔带过&#xff0c;这个图看的云里雾里的&#xff0c;不是非常明白(可能是我理解力的问题&#xff09;&#xff0c;所以&#xff…

AngularJs学习笔记--bootstrap

AngularJs学习笔记系列第一篇&#xff0c;希望我可以坚持写下去。本文内容主要来自 AngularJS 文档的内容&#xff0c;但也加入些许自己的理解与尝试结果。 一、总括 本文用于解释Angular初始化的过程&#xff0c;以及如何在你有需要的时候对Angular进行手工初始化。 二、An…

【云原生-K8s】k8s可视化管理界面安装配置及比较【Kubesphere篇】

总览 安装了k8s控制面板&#xff0c;方便日常的问题处理&#xff0c;查看资源状态信息&#xff0c;也可以增加子账号进行开放给其他人员使用&#xff0c;减少命令操作&#xff0c;提升工作效率 前置条件 须有一个正常使用的k8s集群附k8s v1.23版本搭建&#xff1a;https://…

JavaScript实例(Visual Studio Code)(一)

JavaScript程序本身不能独立存在 它是依附于某个HTML页面 在浏览器端运行的 基本语法&#xff1a; <script type"text/javascript" [src"外部js文件"]>... </script> 语法说明&#xff1a; script为脚本标记&#xff0c;它必须以<scri…

【小沐学Web】Rust实现Web服务器

文章目录 1、简介2、开发环境配置2.1 下载2.2 安装2.3 编辑工具2.4 构建工具2.5 自动化工具 3、Hello World4、TCP/UDP通信5、Web服务器结语 1、简介 https://www.rust-lang.org/ Rust: 一种使每个人都能够构建可靠且高效的软件的语言。 如今&#xff0c;全球有数百家公司在生…

谈谈Memcached与Redis

1. Memcached简介 Memcached是以LiveJurnal旗下Danga Interactive公司的Bard Fitzpatric为首开发的高性能分布式内存缓存服务器。其本质上就是一个内存key-value数据库&#xff0c;但是不支持数据的持久化&#xff0c;服务器关闭之后数 据全部丢失。Memcached使用C语言开发&am…