C++ Primer Plus 学习笔记(第 7 章 函数 —— C++ 的编程模块)

本文介绍了C++中的函数,包括函数定义、原型和调用,强调了函数原型在静态类型检查中的作用。讨论了按值传递参数,特别是与数组和指针的关系,以及函数如何处理二维数组。还涵盖了函数与C风格字符串、结构、string对象和array对象的交互。此外,解释了递归函数和函数指针的概念,展示了如何声明、使用和调用函数指针。
摘要由CSDN通过智能技术生成

C++ Primer Plus 学习笔记

第 7 章 函数 —— C++ 的编程模块

复习函数的基本知识

要使用 C++ 函数,必须完成如下工作:

  • 提供函数定义;
  • 提供函数原型;
  • 调用函数。

库函数是已经定义和编译好的函数,同时可以使用标准库头文件提供原型,因此只需正确调用即可。而自定义函数必须自行处理这 3 个方面——定义、提供原型和调用。

定义函数

可以将函数分成两类:没有返回值的函数和有返回值的函数。
没有返回值的函数被称为void函数,其通用格式如下:

void functionName(parameterList)
{
    statement(s)
    return;         // optional
}

parameterList指定了传递给函数的参数类型和数量,可选返回语句标记了函数的结尾,否则,函数将在右花括号处结束。
有返回值的函数其通用格式如下:

typeName functionName(parameterList)
{
    statements
    return vlaue;         // value is type cast to type typeName
}

对于有返回值的函数,必须使用返回语句,以便将值返回给调用函数。值本身可以是常量、变量,也可以是表达式,只是其结果的类型必须为typeName类型或可以被转换为typeName。函数将最终的值返回给调用函数。C++ 对于返回值的类型有一定的限制:不能是数组,但可以是其他类型——整数、浮点数、指针,甚至可能是结构和对象!(可以将数组作为结构和对象的组成部分返回。)
通常,函数通过将返回值复制到指定的 CPU 寄存器或内存单元中将其返回。随后,调用函数将查看该内存单元。

函数原型和函数调用
为什么需要原型

原型描述了函数到编译器的接口,它将函数返回值的类型(如果有的话)以及参数的类型和数量告诉编译器。
避免使用函数原型的唯一方法是,在首次使用函数之前定义它,但这并不总是可行的。另外,C++ 的编程方格是将main()放在最前面,因为它通常提供了程序的整体结构。

原型的语法

函数原型是一条语句,因此必须以分号结束。获得原型最简单的方法是,复制函数定义中的函数头,并添加分号。
通常,在原型的参数列表中,可以包括变量名,也可以不包括。原型中的变量名相当于占位符,因此不必与函数定义中的变量名相同。
在 C++ 中,原型是必不可少的。参数列表为空和使用关键字void是等效的——意味着函数没有参数。
在 C++ 中,不指定参数列表时应使用省略号,通常,仅当与接受可变参数的 C 函数交互时才需要这样做。

原型的功能

原型确保以下几点:

  • 编译器正确处理函数返回值;
  • 编译器检查使用的参数数目是否正确;
  • 编译器检查使用的参数类型是否正确。如不正确,则转换为正确的类型(如果可能的话)。

C++ 自动将传递的值转换为原型中指定的类型,条件是两者都是算术类型。
自动类型转换并不能避免所有可能的错误。
仅当有意义时,原型化才会导致类型转换。例如,原型不会将整型转换为结构或指针。
在编译阶段进行的原型化被称为静态类型检查(static type checking)。静态类型检查可捕获许多在运行阶段非常难以捕获的错误。

函数参数和按值传递

C++ 通常按值传递参数,将数值参数传递给函数,函数将其赋给一个新的变量。
用于接受传递值的变量被称为形参,传递给函数的值被称为实参。C++ 标准使用参数(argument)来表示实参,使用参量(parameter)来表示形参。
在函数中声明的变量(包括参数)是该函数私有的,被称为局部变量,被限制在函数中。也被称为自动变量。

多个参数

函数可以有多个参数,在调用时,用逗号将这些参数分开。
在定义函数时,在函数头中使用逗号分隔的参数声明列表。如果函数的两个参数类型相同,则必须分别指定每个参数的类型,而不能像声明常规变量那样,将声明组合在一起。
原型中的变量名不必与定义中的变量名相同,而且可以省略。然而,提供变量名将使原型更容易理解,尤其是两个参数的类型相同时。这样,变量名可以提醒参量和参数间的相应关系。
形参与其他局部变量的主要区别是,形参从调用函数那里获得自己的值,而其他变量是从函数中获得自己的值。

函数和数组

函数如何使用指针来处理数组

在大多数情况下,C++ 与 C 语言一样,也将数组名视为指针,该规则有一些例外。首先,数组声明使用数组名来标记存储位置;其次,对数组名使用sizeof()将得到整个数组的长度;第三,将地址运算符&用于数组名时,将返回整个数组的地址。
在 C++ 中,当(且仅当)用于函数头或函数原型中,int arr[]int *arr的含义才是相同的。

arr[i] == *(arr + i)    \\values in two notations
&arr[i] == arr + i      \\addresses in two notations

遍历数组时,使用指针加法和数组下标是等效的。

将数组作为参数意味着什么

将数组作为参数,是将数组的位置(地址)传递给了函数。传递常规变量时,函数将使用该变量的拷贝;但传递数组时,函数将使用原来的数组。这种区别并不违反 C++ 按值传递的方法,传递了一个值,这个值被赋给了一个新的变量,这个值是地址,而不是数组的内容。因此,无法使用sizeof()来获悉原始数组的长度。
数组名与指针对应是一件好事,将数组地址作为参数可以节省复制整个数组的时间和内存。另一方面,使用原始数据增加了破坏数据的风险。

用 const 保护数组

为防止函数无意中修改数组的内容,可在声明形参时使用关键字const。该声明表示,指针指向的的常量数据,不能使用指针修改该数据,但并不意味着原始数组必须是常量。

使用数组区间的函数

处理数组的 C++ 函数,必须将数组中的数据种类、数组的起始位置和数组中元素数量提交给它;舍传统 C/C++ 方法是,将指向数组起始位处的指针作为第一个参数,将数组长度作为第二个参数。
另一种方法是,指定元素区间,可以通过传递两个指针,一个指针标识数组的开头,另一个指针标识数组的结尾。C++ 标准库(STL)将区间方法广义化,使用“超尾”概念来指定区间。也就是说,对于数组而言,标识数组结尾的参数将是指向最后一个元素后面的指针。

指针和 const

可以用两种不同的方式将const关键字用于指针。第一种方式是让指针指向一个常量对象,可以防止使用该指针来修改所指向的值。第二种方法是将指针本身声明为常量,这样可以防止改变指针指向的位置。
可以将常规变量赋给常规指针,也可将常规变量赋给指向const的指针,还可以将const变量赋给指向const的指针,但不能将const的地址赋给常规指针。
只有一层间接关系(如指针指向基本数据类型)时,才可以将非const地址或指针赋给const指针。
注意:如果数据类型本身并不是指针,则可以将const数据或非const数据的地址赋给指向const的指针,但只能将非const数据地址赋给非const指针。

将指针参数声明为指向常量数据的指针有两条理由:

  • 这样可以避免由于无意间修改数据而导致的编程错误;
  • 使用const使用得函数能够处理const和非const实参,否则将只能接受非const数据。

如果条件允许,则应将指针形参声明为指向const的指针。

函数和二维数组

为编写将二维数组作为参数的函数,必须牢记,数组名被视为指针,因此,相应的形参是一个

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值