函数----基础,参数传递,返回类型和return语句
一、函数基础
1、形参和实参
实参是形参的初始值。第一个实参初始化第一个形参,第二个实参初始化第二个形参,以此类推。尽管实参与形参存在对应关系,但是并没有规定实参的求值顺序。编译器能以任意可行的顺序对实参求值。
2、函数返回类型
函数的返回类型不能是数组类型或函数类型,但可以是指向数组或函数的指针。
3、局部对象
对于普通局部变量对应的对象来说,当函数的控制路径经过变量定义语句时创建该对象,当到达定义所在的块末尾时销毁它。我们把只存在于块执行期间的对象称为自动对象。如果自动对象变量定义时本身不含初始值,执行默认初始化。
某些时候,有必要令局部变量的生命周期贯穿函数调用及之后的时间。可以将局部变量定义成static类型从而获得这样的对象。局部静态对象在程序的执行路径第一次经过对象定义语句时初始化,并且直到程序终止才被销毁,在此期间即使对象所在的函数结束执行也不会对它有影响。如果局部静态变量没有显示的初始值,它将执行值初始化,内置类型的局部静态变量初始化为0。
#include <iostream>
int func()
{
static int count = ;
return ++count;
}
int main()
{
for (int i = ; i < ; ++i)
{
std::cout << func() << " ";
}
std::cout << std::endl;
return ;
}
4、函数声明
类似于变量,函数只能定义一次,但可以声明多次。函数的声明和函数的定义非常类似,唯一的区别是函数声明无须函数体,用一个分号替代即可。因为函数的声明不包含函数体,所以可以省略形参的名字。
二、参数传递
形参的类型决定了形参和实参交互的方式。如果形参是引用类型,它将绑定到对应的实参上;否则,将实参的值拷贝后赋给形参。
1、指针形参
指针的行为和其他非引用类型一样。当执行指针拷贝操作时,拷贝的是指针的值。拷贝之后,两个指针是不同的指针。因为指针使我们可以间接地访问它所指的对象,所以通过指针可以修改它所指对象的值。
2、const形参和实参
当用实参初始化形参时会忽略掉顶层const。
3、数组形参
1)为函数传递一个数组时,实际上传递的是指向数组首元素的指针。
void print(const int *);
void print(const int[]);
void print(const int[]);//这里的维度表示我们期望数组含有多少元素,实际不一定
//尽管表现形式不同,但上面的三个函数是等价的:每个函数的唯一形参都是const int*类型的
2)数组引用形参
引用形参绑定到对应的实参上,也就是绑定到数组上。
void print(int(&arr)[]); // 只能将函数作用于大小为10的数组
3)传递多维数组
C++语言中实际上没有真正的多维数组,多维数组其实是数组的数组。和所有数组一样,当将多维数组传递给函数时,真正传递的是指向数组首元素的指针。因为我们处理的是数组的数组,所以首元素本身就是一个数组,指针就是一个指向数组的指针。数组第二维(以及后面所有维度)的大小都是数组类型的一部分,不能省略。
void print(int (*arr)[]); //arr指向数组的首元素,该数组的元素是由10个整数构成的数组
void print(int arr[][]); // arr的声明看起来是一个二维数组,实际上形参是指向含有10个整数的数组的指针
// 上面2个函数等价
4、main:处理命令行选项
有时需要给main传递实参,一种常见的情况是用户通过设置一组选项来确定函数所要执行的操作。例如我们假定main函数位于可执行文件DailyProject之内,我们可以向程序传递下面的选项:
DailyProject hello world
这些命令行选项通过两个可选的形参传递给main函数:
#include <iostream>
#include <string> int main(int argc, char *argv[])
{
std::cout << "argc:" << argc << std::endl;
for (auto i = ; i < argc; ++i)
{
std::cout << "argv[" << i << "]:" << argv[i] << std::endl;
}
return ;
}
第一个形参argc表示数组中字符串的数量;第二个形参argv是一个数组,它的元素是指向C风格字符串的指针。
当实参传递给main函数之后,argv的第一个元素指向程序的可执行文件的名字,接下来的元素依次传递命令行提供的实参。
5、含有可变形参的函数
为了编写能处理不同数量实参的函数,C++11新标准提供了两种主要的方法:如果所有的实参类型相同,可以传递一个名为initializer_list的标准库类型;如果实参的类型不同,我们可以编写一种特殊的函数,也就是所谓的可变参数模板(618)。
1)initializer_list形参
如果函数的实参数量未知但是全部实参的类型都相同,我们可以使用initializer_list类型的实参。initializer_list是一种标准库类型,用于表示某种特定类型的值的数组。initializer_list定义在同名的头文件中,它提供的操作如下表所示:
操作 | 说明 |
initializer_list<T> lst | 默认初始化;T类型元素的空列表 |
initializer_list<T> lst{a,b,c,..} | lst的元素数量和初始值一样多;lst的元素是对应初始值的副本;列表中的元素是const |
lst2(lst) | 拷贝或赋值一个initializer_list对象不会拷贝列表中的元素;拷贝后,原始列表和副本共享元素 |
lst2=lst | 拷贝或赋值一个initializer_list对象不会拷贝列表中的元素;拷贝后,原始列表和副本共享元素 |
lst.size() | 列表中元素的数量 |
lst.begin() | 返回指向lst中首元素的位置 |
lst.end() | 返回指向lst中尾元素下一位置的指针 |
和vector一样,initializer_list也是一种模板类型。定义initializer_list对象时,必须说明列表中所含元素的类型。和vector不一样的是,initializer_list对象中的元素永远是常量值,我们无法改变initializer_list对象中元素的值。
如果想向initializer_list形参中传递一个值的序列,则必须把序列放在一对花括号内。
#include <iostream>
#include <string>
#include <initializer_list> void error_msg(std::initializer_list<std::string> lst)
{
for (auto it = lst.begin(); it!=lst.end(); ++it)
{
std::cout << *it << std::endl;
}
}
int main(int argc, char *argv[])
{
error_msg({ "hello", "world", "QAQ" });
return ;
}
三、返回类型和return语句
return语句终止当前正在执行的函数并将控制权返回到调用函数的地方。return语句有两种形式。
return;
return expression;
1、无返回值函数
没有返回值的return语句只能用在返回类型是void的函数中。返回void的函数不要求非得有return语句,因为在这类函数的最后一句后面会隐式地执行return。
一个返回类型是void的函数也能使用return语句的第二种形式,不过此时return语句的expression必须是另一个返回void的函数。强行令void函数返回其他类型的表达式将产生编译错误。
2、有返回值函数
return语句的第二种形式提供了函数的结果。只要函数的返回类型不是void,则该函数内的每条return语句必须返回一个值。return语句返回值的类型必须与函数的返回类型相同,或者能隐式地转换成函数的返回类型。
不要返回局部对象的引用和指针。
和其他运算符一样,调用运算符也有优先级和结合律。调用运算符的优先级与点运算符和箭头运算符相同,并且也符合左结合律。因此,如果函数返回指针、引用或类的对象,我们就能使用函数调用的结果访问结果对象的成员。
函数的返回类型决定函数调用是否是左值。调用一个返回引用的函数得到左值,其他返回类型得到右值。可以像使用其他左值那样来使用返回引用的函数的调用,特别是,我们能为返回类型是非常量引用的函数的结果赋值。
C++11新标准规定,函数可以返回花括号包围的值的列表。类似于其他返回结果,此处的列表也用来表示函数返回的临时变量进行初始化。如果列表为空,临时变量执行值初始化;否则,返回的值由函数的返回类型决定。
3、返回数组指针
1)使用类型别名
从语法上来说,定义一个返回数组的指针或引用的函数比较繁琐,但是也有一些方法可以简化这一任务,其中最直接的方法是使用类型别名:
typedef int arrT[]; // arrT是一个类型别名,它表示的类型是含有10个整数的数组
using arrT2 = int[]; // arrT的等价声明
arrT *func(); // 返回一个指向含有10个整数的数组指针
2)声明一个返回数组指针的函数
如果我们想定义一个返回数组指针的函数,则数组的维度必须跟在函数名字之后。然而,函数的形参列表也跟在函数名字后面且形参列表应该是先于数组的维度。因此,返回数组指针的函数形式如下所示:
Type (*function(parameter_list))[dimension]
Type表示元素的类型,dimension表示数组的大小。(*function(parameter_list))两端的括号必须存在,如果没有这对括号,函数的返回类型将是指针的数组。
int(*func(int i))[];
可以按照以下的顺序来逐层理解该声明的含义:
func(int i)表示调用func函数时需要一个int类型的实参;
(*func(int i))表示我们可以对函数调用的结果执行解引用操作;
(*func(int i))[10]表示解引用func的调用将得到一个大小是10的数组;
int (*func(int i))[10]表示数组中的元素是int类型。
3)使用尾置返回类型
在C++11新标准中还有一种可以简化上述func声明的方法,就是使用尾置返回类型。任何函数的定义都能使用尾置返回,但是这种形式对于返回类型比较复杂的函数最有效,比如返回类型是数组的指针或者数组的引用。尾置返回类型跟在形参表后面并以一个->符号开头。为了表示函数真正的返回类型跟在形参列表之后,我们在本应该出现返回类型的地方放置一个auto:
auto func(int i) -> int(*)[]; // 返回一个指向10个整数的数组的指针
4)使用decltype
如果我们知道函数返回的指针指向哪个数组,就可以使用decltype关键字声明返回类型。
#include <iostream> int arr[] = { , , };
decltype(arr) *func()
{
return &arr;
}
int main()
{
std::cout << (*func())[] << std::endl;
return ;
}
注意:decltype并不负责把数组类型转换成对应的指针,所以decltype的结果是个数组,要想表示func返回数组还必须在函数声明时加上一个*符号。
函数----基础,参数传递,返回类型和return语句的更多相关文章
- 返回类型和return语句
return语句终止当前正在执行的函数并将控制权返回到调用该函数的地方.return语句有两种形式: return; return expression; 无返回值函数 没有返回值的return语句只 ...
- 【c++ primer, 5e】返回类型和return语句
[无返回值函数] 1.在c++的void函数中,可以显式地使用return;语句来提前结束函数的调用. [有返回值函数] 1.值是如何被返回的:返回一个值的方式和初始化一个变量或者形参的方式完全一样. ...
- 返回类型和 return 语句
return 语句终止当前正在执行的函数并将控制权返回到调用该函数的地方.return 语句有两种形式: return; return expression; 不要返回局部对象的引用或指针: 函数完成 ...
- python基础----函数的定义和调用、return语句、变量作用域、传参、函数嵌套、函数对象、闭包、递归函数
1.函数的定义: 函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段. 函数能提高应用的模块性,和代码的重复利用率.你已经知道Python提供了许多内建函数,比如print().但你也可 ...
- python函数基础-参数-返回值-注释-01
什么是函数 函数就是有特定功能的工具 # python中有内置函数(python解释器预先封装好的)与自定义函数(用户自定义封装的)之分 为什么要用函数 # 可以减少代码冗余,增加代码复用性 # 使代 ...
- 关于类中的参数类型和return返回值
基础有些忘了,现在重新巩固一下 先定义一个Person类 class Person(): def __init__(self,name,age,height): self.name=name, sel ...
- js基础回顾-数据类型和typeof怎么用
js的基本数据类型有六种,undefined.null.number.string.boolean.object. 未定义 空 数字 字符串 布尔 ...
- ORA-04044: 此处不允许过程, 函数, 程序包或类型和
用Orale代码建表时,出现 SQL> comment on column SCORE.cno 2 is '学号(外键)';comment on column SCORE.cno is '学号( ...
- 第五篇:Python函数基础篇
本篇介绍什么是函数.函数的特性.函数的定义.函数的调用.以及函数的参数.以及关于全局变量和局部变量的使用等等. 一.什么是函数: 函数是最基本的一种代码抽象方式,为了实现某种特定的功能而组织的带名字的 ...
随机推荐
- python 部分函数
abs(number) ,返回数字的绝对值cmath.sqrt(number) ,返回平方根,也可以应用于负数float(object) ,把字符串和数字转换为浮点数help() ,提供交互式帮助in ...
- [原][粒子特效][spark]深入浅出osgSpark
背景: 目前我使用的spark粒子特效库是2.0 这个库好像是原来鬼火引擎的一部分,需要从github上找 现在我要将其使用到我自己开发的基于osgearth开的三维地图引擎中 步骤: 1.编译spa ...
- maven项目, 单元测试失败提示 Class not found datastorage........
---恢复内容开始--- 单元测试失败: 提示 Class not found datastorage........ 原因: maven 环境变量问题, eclipse 没有自动更新下载 ...
- JAVA实操项目:转账接口设计
在一个项目中,一般都会支付相关的业务,而涉及到支付必定会有转账的操作,转账这一步想起来算是比较关键的部分,这个接口的设计能力,也大致体现出一个人的水平. 昨天碰到了一个题目: 尝试用java编写一个转 ...
- leecode第一百零四题(二叉树的最大深度)
/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode ...
- MySQL学习(十一)
MySQL的函数 1 数学函数 2 字符串函数 3 日期和时间函数 4 条件判断函数 5 系统信息函数 6 加密解密函数 7 其他函数 2 字符串函数 length计算的是字节长度 char_leng ...
- java操作vaftpd实现上传、下载
1.配置文件conf/vsftpd.properties (我是单独写了一个配置文件,你可以直接写在application中) vsftpd.ip=192.168.**.** vsftpd.user= ...
- linux基础05-管道及IO重定向
(1)I/O重定向:Linux:>: 覆盖输出>>:追加输出 (2)set -C: 禁止对已经存在文件使用覆盖重定向: 强制覆盖输出,则使用 >|set +C: 关闭上述功能 ...
- zlib 2.1.8 编译遇到的问题以及解决方法
环境:win7 x64 + vs2013 1.用vs2013打开zlib-1.2.8\contrib\vstudio\vc11\zlibvc.sln进行编译 包含了下面的的多个项目: miniunz: ...
- 第 7 章 多主机管理 - 046 - 创建 Machine
创建 Machine Machine 就是运行 docker daemon 的主机. “创建 Machine” 指的就是在 host 上安装和部署 docker. 创建第一个 machine: hos ...