C++ Primer Plus学习(七)——函数简介
函数简介
- 函数的基本知识
- 函数参数和按值传递
- 函数和数组
- 函数和C-风格字符串
- 函数和结构
- 递归
- 函数指针
- 总结
函数的基本知识
- 创建函数时,必须完成如下工作:提供函数定义;提供函数原型;调用函数。
- C++对于函数返回值的类型有一定的限制:不能是数组,但可以是其他任何类型——整数、浮点数、指针,甚至可以是结构和对象。
- 函数原型不要求提供变量名,有类型列表就足够了。通常,在原型的参数列表中,可以包括变量名,也可以不包括。原型中的变量名相当于占位符,因此不必与函数定义中的变量名相同。
- 函数原型的功能:
- 编译器正确处理函数返回值;
- 编译器检查使用的参数数目是否正确;
- 编译器检查使用的参数类型是否正确,如果不正确,则转换为正确的类型(如果可能的话)。在编译阶段进行的原型化被称为静态类型检查(static type checking)。
函数参数和按值传递
- 用于接收传递值的变量被称为形参;传递给函数的值被称为实参。
- 如果函数的两个参数的类型相同,则必须分别指定每个参数的类型,而不能像声明常规变量那样,将声明组合在一起;原型中的变量名不必与定义中的变量名相同,而且可以省略;提供变量名将使原型更容易理解,尤其是两个参数的类型相同时。
- 对于
(10 * 9) / (2 * 1)
和(10 / 2) * (9 / 1)
,前者将计算90 / 2
,得到45;后者将计算为5 * 9
,得到45。当数字非常大时,这种交替进行乘除运算的策略可以防止中间结果超出最大的浮点数。
函数和数组
在C++中,当(且仅当)用于函数头或函数原型中,
int *arr
和int arr[]
的含义才是相同的。它们都意味着arr
是一个int
指针。将指针(包括数组名)加1,实际上是加上了一个与指针指向的类型的长度(以字节为单位)相等的值。对于遍历数组而言,使用指针加法和数组下标时是等效的。传递常规变量时,函数将使用该变量的拷贝;但传递数组时,函数将使用原来的数组。实际上,这种区别并不违反C++按值传递的方法,函数仍传递一个值,这个值被赋给一个新变量,但这个值是一个地址,而不是数组的内容。
将数组地址作为参数可以节省复制整个数组所需的时间和内存。
对于处理数组的C++函数,必须将数组中的数据种类、数组的起始位置和数组中元素数量提交给它;传统的C/C++方法是,将指向数组起始处的指针作为一个参数,将数组长度作为第二个参数(指针指出数组的位置和数据类型);还有另一种给函数提供所需信息的方法,即指定元素区间(range),这可以通过传递两个指针来完成:一个指针标识数组的开头,另一个指针标识数组的尾部。
写个例子加深一下各方面理解:
#include <iostream> const int Max = 5; int fill_array(double ar[], int limit); void show_array(const double ar[], int n); // do not change data void revalue(double r, double ar[], int n);int main() {using namespace std;double properties[Max];int size = fill_array(properties, Max);show_array(properties, size);if (size > 0){cout << "Enter revaluation factor: ";double factor;while (!(cin >> factor)) // bad input{cin.clear();while (cin.get() != '\n')continue;cout << "Bad input; Please enter a number: ";}revalue(factor, properties, size);show_array(properties, size);}cout << "Done.\n";cin.get();cin.get();return 0; }int fill_array(double ar[], int limit) {using namespace std;double temp;int i;for (i = 0; i < limit; i++){cout << "Enter value #" << (i + 1) << ": ";cin >> temp;if (!cin) // bad input{cin.clear();while (cin.get() != '\n')continue;cout << "Bad input; input process terminated.\n";break;}else if (temp < 0) // signal to terminatebreak;/// 如果输入错误或者负值,则终止输入,返回当前输入值的数量;否则将输入值增加到数组中ar[i] = temp;}return i; }void show_array(const double ar[], int n) {using namespace std;for (int i = 0; i < n; i++){cout << "Property #" << (i + 1) << ": $";cout << ar[i] << endl;} }void revalue(double r, double ar[], int n) {for (int i = 0; i < n; i++)ar[i] *= r; }
指针和
const
可以用两种不同的方式将
const
关键字用于指针。第一种方法是让指针指向一个常量对象,这样可以防止使用该指针来修改所指向的值,第二种方法是将指针本身声明为常量,这样可以防止改变指针指向的位置。举个例子来说明一下:
int age = 39; const int * pt = &age;
pt
的声明并不意味着它指向的值实际上就是一个常量,而只是意味着对pt
而言,这个值是常量。例如,pt
指向age
,而age
不是const
。可以直接通过age
变量来修改age
的值,但不能使用pt
指针来修改它。同时,该声明中的const
只能防止修改pt
的值(这里是39),而不能防止修改pt
的值。也就是说,可以将一个新的地址赋给pt
。再比如:
int sloth = 3; const int * ps = &sloth; // a pointer to const int int * const finger = &sloth; // a const pointer to int
在最后一个声明中,关键字
const
的位置与以前不同,这种声明格式使得finger
只能指向sloth
,但允许使用finger
来修改sloth
的值。也就是,地址是常量。如果愿意的话,还可以声明指向
const
对象的const
指针:double trouble = 2.98; const double * const stick = &trouble;
综上来讲,其实有四种组合关系:将常规变量的地址赋给常规指针;将常规变量的地址赋给指向
const
的指针;将const
变量的地址赋给指向const
的指针;将const
的地址赋给常规指针。这里面,第四种是不可行的,其他都可行。将指针参数声明为指向常量数据的指针有两条理由:a. 这样可以避免由于无意间修改数据而导致的编程错误;b. 使用
const
使得函数能够处理const
和非const
实参,否则将只能接受非const
数据。如果条件允许,则应将指针形参声明为指向const
的指针。
函数和C-风格字符串
- 将字符串作为参数时意味着传递的是地址,但可以使用
const
来禁止对字符串参数进行修改。 - C-风格字符串有内置的结束字符,这意味着不必将字符串长度作为参数传递给函数,而函数可以使用循环依次检查字符串中的每个字符,直到遇到结尾的空值字符为止。
函数和结构
涉及到函数传递时,结构变量的行为更接近于基本的单值变量:
- 可以将一个结构赋给另一个结构;
- 可以按值传递结构,就像普通变量那样;在这种情况下,函数将使用原始结构的副本;函数也可以返回结构;但这样有一个缺点,如果结构非常大,则复制结构将增加内存要求,降低系统运行的速度;
- 也可以传递结构的地址,用指针来访问结构的地址;与数组名就是数组第一个元素的地址不同的是,结构名只是结构的名称,要获得结构的地址,必须使用地址运算符
&
; - 另外,C++还使用
&
表示引用变量,按引用传递也是特别重要的一种方式。
递归
函数指针
总结
函数是C++的编程模块。要使用函数,必须提供定义和原型,并调用该函数。函数定义是实现函数功能的代码;函数原型描述了函数的接口; 传递给函数的值的数目和种类以及函数的返回类型,函数调用使得程序将参数传递给函数,并执行函数的代码。
在默认情况下,C++函数按值传递参数。这意味着函数定义中的形参是新的变量,它们被初始化为函数调用所提供的值。因此,C++函数通过使用拷贝,保护了原始数据的完整性。
C++将数组名参数视为数组第一个元素的地址。从技术上讲,这仍然是按值传递的,因为指针是原始地址的拷贝,但函数将使用指针来访问原始数组的内容。当且仅当声明函数的形参时,下面两个声明才是等价的:
typeName arr[];
typeName * arr;
这两个声明都表明,arr
是指向typeName
的指针,但在编写函数代码时,可以像使用数组名那样使用arr
来访问元素:arr[i]
。即使在传递指针时,也可以将形参声明为const
指针,来保护原始数据的完整性。由于传递数据的地址时,并不会传输有关数组长度的信息,因此通常将数组长度作为独立的参数来传递。另外,也可传递两个指针(其中一个指向数组开头,另一个指向数组末尾的下一个元素),以指定一个范围,就像STL使用的算法一样。
C++提供了3种表示C-风格字符串的方法:字符数组、字符串常量和字符串指针。它们的类型都是char*
(char
指针),因此被作为char*
类型参数传递给函数。C++使用空值字符(\0
)来结束字符串,因此字符串函数检测空值字符来确定字符串的结尾。
C++还提供了string
类,用于表示字符串。函数可以接受string
对象作为参数以及将string
对象作为返回值。string
类的方法size()
可用于判断其存储的字符串的长度。
C++处理结构的方式与基本类型完全相同,这意味着可以按值传递结构,并将其用作函数返回类型。然而,如果结构非常大,则传递结构指针的效率将更高,同事函数能够使用原始数据。这些考虑因素也适用于类对象。
C++函数可以是递归的,也就是说,函数代码中可以包括对函数本身的调用。
C++函数名与函数地址的作用相同。通过将函数指针作为参数,可以传递要调用的函数的名称。
C++ Primer Plus学习(七)——函数简介相关推荐
- 整理:C primer plus 学习笔记
前言:简单看了一遍C Primer Plus, 整理了一下,因为时间比较少,自己理解地比较肤浅,所以第一版比较简陋. 假期的时候应该会有时间再整理一下.------2018/11/5 2019/1/2 ...
- C++ Primer Plus 学习笔记(第 4 章 复合类型)
C++ Primer Plus 学习笔记 第 4 章 复合类型 数组 数组(array)是一种数据格式,能够存储多个同类型的值. 要创建数组,可使用声明语句.数组声明应指出以下三点: 存储在每个元素的 ...
- c语言实验七 函数实验报告,C语言实验七函数实验报告.doc
C语言实验七函数实验报告 C语言程序设计 实 验 报 告 实验题目 实验七 函数 实验目的 掌握函数定义的方法: 掌握函数实参与形参的对应关系,以及值传递的方式. 掌握函数的嵌套调用和递归调用的方法: ...
- TypeScript基础入门 - 函数 - 简介
2019独角兽企业重金招聘Python工程师标准>>> 转载 TypeScript基础入门 - 函数 - 简介 项目实践仓库 https://github.com/durban89/ ...
- Python之sklearn:LabelEncoder函数简介(编码与编码还原)、使用方法、具体案例之详细攻略
Python之sklearn:LabelEncoder函数简介(编码与编码还原).使用方法.具体案例之详细攻略 目录 LabelEncoder函数的简介(编码与编码还原) Methods LabelE ...
- DL:深度学习(神经网络)的简介、基础知识(神经元/感知机、训练策略、预测原理)、算法分类、经典案例应用之详细攻略
DL:深度学习(神经网络)的简介.基础知识(神经元/感知机.训练策略.预测原理).算法分类.经典案例应用之详细攻略 目录 深度学习(神经网络)的简介 1.深度学习浪潮兴起的三大因素 深度学习(神经网络 ...
- 如何利用《C++ Primer》学习C++?
<C++ Primer>作为久负盛名的C++经典教程,丰富的教学辅助内容.精心组织的编程示范,无论是初学者入门,或是中.高级程序员提升,都是不容置疑的首选. 一本好书只有读过才有价值,然而 ...
- OpenCV与图像处理学习七——传统图像分割之阈值法(固定阈值、自适应阈值、大津阈值)
OpenCV与图像处理学习七--传统图像分割之阈值法(固定阈值.自适应阈值.大津阈值) 一.固定阈值图像分割 1.1 直方图双峰法 1.2 OpenCV中的固定阈值分割 二.自动阈值图像分割 2.1 ...
- PyTorch框架学习七——自定义transforms方法
PyTorch框架学习七--自定义transforms方法 一.自定义transforms注意要素 二.自定义transforms步骤 三.自定义transforms实例:椒盐噪声 虽然前面的笔记介绍 ...
最新文章
- webpack 使用别名(resolve.alias)解决scss @import相对路径导致的问题
- Spring中AOP相关的API及源码解析,原来AOP是这样子的
- 【MM】需求类型清单
- html中表单元素_HTML中的表单元素
- 11-散列3 QQ帐户的申请与登陆 (25 分)
- Docker中搭建FastDFS文件系统(多图)
- 编解码标准H264 与 AVS 变换矩阵比较
- 计算机 旧词新说_如何使旧计算机再次有用
- [Web Chart系列之一(续)]Web端图形绘制SVG,VML, HTML5 Canvas 简单实例
- 月薪11.5K 土木人零基础转行软件测试工程师,他都经历了什么?
- OpenGL调用GPU(七)
- vs2005新建项目中没有ASP.NET WEB应用程序的解决方法
- Linux之kill命令
- 视频,多媒体本地化总结
- 软件工程导论学习总结
- 88年的世界杯历史,用Python带你回顾!
- ubuntu GStreamer + QT多媒体播放器开发(二)
- 移动端touch事件影响界面click/超链接事件无法点击
- Mac如何录屏 同时录内置声音
- dataframe两个表合并_DataFrame踩坑整理(一)
热门文章
- bzoj 2351: [BeiJing2011]Matrix(二维Hash)
- bzoj 3503: [Cqoi2014]和谐矩阵(高斯消元)
- bzoj 1622: [Usaco2008 Open]Word Power 名字的能量
- bzoj 1670: [Usaco2006 Oct]Building the Moat护城河的挖掘(凸包)
- opencv 寻找图中的corners 利用自带 Shi-Tomasi Corner Detector 实现
- javascript学习之利用方向键控制div模块的移动
- [Python] numpy.ndenumerate() 获得一对数组坐标和值
- 2020 各大厂分享ppt
- 深度学习:文本检测数据集整理
- 汇编三星题目:一个有符号字数组以0为结束标志,编程求这个数组的最大值、 最小值、平均值